| 1 |
/* |
| 2 |
* Copyright (c) 2021-2022 joshua stein <jcs@jcs.org> |
| 3 |
* |
| 4 |
* Permission to use, copy, modify, and distribute this software for any |
| 5 |
* purpose with or without fee is hereby granted, provided that the above |
| 6 |
* copyright notice and this permission notice appear in all copies. |
| 7 |
* |
| 8 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 9 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 10 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 11 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 12 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 13 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 14 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 15 |
*/ |
| 16 |
|
| 17 |
#include <stdio.h> |
| 18 |
#include <string.h> |
| 19 |
#include <time.h> |
| 20 |
|
| 21 |
#include "subtext.h" |
| 22 |
#include "board.h" |
| 23 |
#include "bile.h" |
| 24 |
#include "db.h" |
| 25 |
#include "folder.h" |
| 26 |
#include "logger.h" |
| 27 |
#include "mail.h" |
| 28 |
#include "main_menu.h" |
| 29 |
#include "user.h" |
| 30 |
#include "util.h" |
| 31 |
|
| 32 |
struct struct_field config_fields[] = { |
| 33 |
{ "BBS Name", CONFIG_TYPE_STRING, |
| 34 |
offsetof(struct config, name), |
| 35 |
1, member_size(struct config, name) }, |
| 36 |
{ "Phone Number", CONFIG_TYPE_STRING, |
| 37 |
offsetof(struct config, phone_number), |
| 38 |
1, member_size(struct config, phone_number) }, |
| 39 |
{ "Location", CONFIG_TYPE_STRING, |
| 40 |
offsetof(struct config, location), |
| 41 |
1, member_size(struct config, location) }, |
| 42 |
{ "Timezone (Abbrev)", CONFIG_TYPE_STRING, |
| 43 |
offsetof(struct config, timezone), |
| 44 |
1, member_size(struct config, timezone) }, |
| 45 |
{ "Timezone (UTC Offset)", CONFIG_TYPE_SHORT, |
| 46 |
offsetof(struct config, timezone_utcoff), |
| 47 |
-1200, 1400 }, |
| 48 |
{ "Allow Open Signup", CONFIG_TYPE_BOOLEAN, |
| 49 |
offsetof(struct config, open_signup), |
| 50 |
0, 0 }, |
| 51 |
|
| 52 |
{ "Hostname", CONFIG_TYPE_STRING, |
| 53 |
offsetof(struct config, hostname), |
| 54 |
1, member_size(struct config, hostname) }, |
| 55 |
{ "Telnet Port", CONFIG_TYPE_SHORT, |
| 56 |
offsetof(struct config, telnet_port), |
| 57 |
0, 65535, |
| 58 |
CONFIG_REQUIRES_TELNET_REINIT }, |
| 59 |
|
| 60 |
{ "Telnet Trusted Proxy IP", CONFIG_TYPE_IP, |
| 61 |
offsetof(struct config, trusted_proxy_ip), |
| 62 |
0, 1, |
| 63 |
CONFIG_REQUIRES_TELNET_REINIT }, |
| 64 |
{ "Telnet Trusted Proxy UDP Port", CONFIG_TYPE_SHORT, |
| 65 |
offsetof(struct config, trusted_proxy_udp_port), |
| 66 |
0, 65535, |
| 67 |
CONFIG_REQUIRES_TELNET_REINIT }, |
| 68 |
{ "IP Geolocation Database Path", CONFIG_TYPE_STRING, |
| 69 |
offsetof(struct config, ipdb_path), |
| 70 |
0, member_size(struct config, ipdb_path), |
| 71 |
CONFIG_REQUIRES_IPDB_REINIT }, |
| 72 |
|
| 73 |
{ "Syslog Server IP", CONFIG_TYPE_IP, |
| 74 |
offsetof(struct config, syslog_ip), |
| 75 |
0, 1, |
| 76 |
CONFIG_REQUIRES_SYSLOG_REINIT }, |
| 77 |
|
| 78 |
{ "Modem Port", CONFIG_TYPE_SHORT, |
| 79 |
offsetof(struct config, modem_port), |
| 80 |
0, 2, |
| 81 |
CONFIG_REQUIRES_SERIAL_REINIT }, |
| 82 |
{ "Modem Port Speed", CONFIG_TYPE_LONG, |
| 83 |
offsetof(struct config, modem_speed), |
| 84 |
300, 115200, |
| 85 |
CONFIG_REQUIRES_SERIAL_REINIT }, |
| 86 |
{ "Modem Bits/Parity/Stop", CONFIG_TYPE_STRING, |
| 87 |
offsetof(struct config, modem_parity), |
| 88 |
1, member_size(struct config, modem_parity), |
| 89 |
CONFIG_REQUIRES_SERIAL_REINIT }, |
| 90 |
{ "Modem Init String", CONFIG_TYPE_STRING, |
| 91 |
offsetof(struct config, modem_init), |
| 92 |
1, member_size(struct config, modem_init), |
| 93 |
CONFIG_REQUIRES_SERIAL_REINIT }, |
| 94 |
{ "Modem Answer After Rings", CONFIG_TYPE_SHORT, |
| 95 |
offsetof(struct config, modem_rings), |
| 96 |
1, 100 }, |
| 97 |
|
| 98 |
{ "Max Idle Minutes", CONFIG_TYPE_SHORT, |
| 99 |
offsetof(struct config, max_idle_minutes), |
| 100 |
0, USHRT_MAX }, |
| 101 |
{ "Max Sysop Idle Minutes", CONFIG_TYPE_SHORT, |
| 102 |
offsetof(struct config, max_sysop_idle_minutes), |
| 103 |
0, USHRT_MAX }, |
| 104 |
{ "Max Login Seconds", CONFIG_TYPE_SHORT, |
| 105 |
offsetof(struct config, max_login_seconds), |
| 106 |
1, USHRT_MAX }, |
| 107 |
|
| 108 |
{ "Screen Blanker Idle Seconds", CONFIG_TYPE_SHORT, |
| 109 |
offsetof(struct config, blanker_idle_seconds), |
| 110 |
0, USHRT_MAX }, |
| 111 |
{ "Screen Blanker Runtime Seconds", CONFIG_TYPE_SHORT, |
| 112 |
offsetof(struct config, blanker_runtime_seconds), |
| 113 |
1, USHRT_MAX }, |
| 114 |
|
| 115 |
{ "Prune Session Logs After N Days", CONFIG_TYPE_SHORT, |
| 116 |
offsetof(struct config, session_log_prune_days), |
| 117 |
0, USHRT_MAX }, |
| 118 |
{ "Prune Mail After N Days", CONFIG_TYPE_SHORT, |
| 119 |
offsetof(struct config, mail_prune_days), |
| 120 |
0, USHRT_MAX }, |
| 121 |
|
| 122 |
{ "FTN Network Name", CONFIG_TYPE_STRING, |
| 123 |
offsetof(struct config, ftn_network), |
| 124 |
0, member_size(struct config, ftn_network), |
| 125 |
CONFIG_REQUIRES_BINKP_REINIT }, |
| 126 |
{ "FTN Local Node Address", CONFIG_TYPE_STRING, |
| 127 |
offsetof(struct config, ftn_node_addr), |
| 128 |
0, member_size(struct config, ftn_node_addr), |
| 129 |
CONFIG_REQUIRES_BINKP_REINIT }, |
| 130 |
{ "FTN Hub Node Address", CONFIG_TYPE_STRING, |
| 131 |
offsetof(struct config, ftn_hub_addr), |
| 132 |
0, member_size(struct config, ftn_hub_addr), |
| 133 |
CONFIG_REQUIRES_BINKP_REINIT }, |
| 134 |
{ "FTN Hub Packet Password", CONFIG_TYPE_PASSWORD, |
| 135 |
offsetof(struct config, ftn_hub_pkt_password), |
| 136 |
0, member_size(struct config, ftn_hub_pkt_password) }, |
| 137 |
{ "FTN Hub Binkp Hostname", CONFIG_TYPE_STRING, |
| 138 |
offsetof(struct config, binkp_hostname), |
| 139 |
0, member_size(struct config, binkp_hostname), |
| 140 |
CONFIG_REQUIRES_BINKP_REINIT }, |
| 141 |
{ "FTN Hub Binkp Port", CONFIG_TYPE_SHORT, |
| 142 |
offsetof(struct config, binkp_port), |
| 143 |
0, 65535 }, |
| 144 |
{ "FTN Hub Binkp Password", CONFIG_TYPE_PASSWORD, |
| 145 |
offsetof(struct config, binkp_password), |
| 146 |
0, member_size(struct config, binkp_password) }, |
| 147 |
{ "FTN Hub Binkp Poll Seconds", CONFIG_TYPE_LONG, |
| 148 |
offsetof(struct config, binkp_interval_seconds), |
| 149 |
1, LONG_MAX }, |
| 150 |
{ "FTN Delete Packets After Processing", CONFIG_TYPE_BOOLEAN, |
| 151 |
offsetof(struct config, binkp_delete_done), |
| 152 |
0, 0 }, |
| 153 |
{ "FTN Max Tossed Message Size", CONFIG_TYPE_LONG, |
| 154 |
offsetof(struct config, ftn_max_tossed_message_size), |
| 155 |
0, LONG_MAX }, |
| 156 |
}; |
| 157 |
size_t nconfig_fields = nitems(config_fields); |
| 158 |
|
| 159 |
struct db * db_init(Str255 file, short vrefnum, struct bile *bile); |
| 160 |
short db_migrate(struct db *tdb, short is_new, Str255 basepath); |
| 161 |
void db_config_load(struct db *tdb); |
| 162 |
const char * db_view_filename(short id); |
| 163 |
|
| 164 |
struct db * |
| 165 |
db_open(Str255 file, short vrefnum, bool ignore_last) |
| 166 |
{ |
| 167 |
Str255 filepath; |
| 168 |
struct stat sb; |
| 169 |
Point pt = { 75, 100 }; |
| 170 |
SFReply reply = { 0 }; |
| 171 |
SFTypeList types; |
| 172 |
StringHandle lastfileh; |
| 173 |
struct db *ret; |
| 174 |
|
| 175 |
if (file[0]) { |
| 176 |
getpath(vrefnum, file, filepath, true); |
| 177 |
if (FStat(filepath, &sb) == 0) |
| 178 |
return db_init(file, vrefnum, NULL); |
| 179 |
|
| 180 |
warn("Failed to open %s", PtoCstr(file)); |
| 181 |
} |
| 182 |
|
| 183 |
if (!ignore_last && (lastfileh = GetString(STR_LAST_DB))) { |
| 184 |
HLock(lastfileh); |
| 185 |
memcpy(filepath, *lastfileh, sizeof(filepath)); |
| 186 |
HUnlock(lastfileh); |
| 187 |
ReleaseResource(lastfileh); |
| 188 |
if (FStat(filepath, &sb) == 0) { |
| 189 |
ret = db_init(filepath, 0, NULL); |
| 190 |
return ret; |
| 191 |
} |
| 192 |
} |
| 193 |
|
| 194 |
/* no file passed, no last file, prompt */ |
| 195 |
types[0] = DB_TYPE; |
| 196 |
SFGetFile(pt, NULL, NULL, 1, &types, NULL, &reply); |
| 197 |
if (reply.good) { |
| 198 |
ret = db_init(reply.fName, reply.vRefNum, NULL); |
| 199 |
return ret; |
| 200 |
} |
| 201 |
|
| 202 |
return db_create(); |
| 203 |
} |
| 204 |
|
| 205 |
struct db * |
| 206 |
db_create(void) |
| 207 |
{ |
| 208 |
Point pt = { 75, 100 }; |
| 209 |
SFReply reply; |
| 210 |
struct bile *bile; |
| 211 |
short error = 0; |
| 212 |
|
| 213 |
SFPutFile(pt, "\pCreate new Subtext DB:", "\p", NULL, &reply); |
| 214 |
if (!reply.good) |
| 215 |
return NULL; |
| 216 |
|
| 217 |
bile = bile_create(reply.fName, reply.vRefNum, SUBTEXT_CREATOR, |
| 218 |
DB_TYPE); |
| 219 |
if (bile == NULL && bile_error(NULL) == dupFNErr) { |
| 220 |
error = FSDelete(reply.fName, reply.vRefNum); |
| 221 |
if (error) |
| 222 |
panic("Failed to re-create file %s: %d", PtoCstr(reply.fName), |
| 223 |
error); |
| 224 |
bile = bile_create(reply.fName, reply.vRefNum, SUBTEXT_CREATOR, |
| 225 |
DB_TYPE); |
| 226 |
} |
| 227 |
|
| 228 |
if (bile == NULL) |
| 229 |
panic("Failed to create file %s: %d", PtoCstr(reply.fName), error); |
| 230 |
|
| 231 |
return db_init(reply.fName, reply.vRefNum, bile); |
| 232 |
} |
| 233 |
|
| 234 |
struct db * |
| 235 |
db_init(Str255 path, short vrefnum, struct bile *bile) |
| 236 |
{ |
| 237 |
Str255 fullpath, newfullpath, viewsdir; |
| 238 |
struct db *tdb; |
| 239 |
Handle lastfileh; |
| 240 |
short was_new, error; |
| 241 |
long dirid; |
| 242 |
|
| 243 |
was_new = (bile != NULL); |
| 244 |
|
| 245 |
if (bile == NULL) { |
| 246 |
bile = bile_open(path, vrefnum); |
| 247 |
if (bile == NULL) { |
| 248 |
if (ask("Attempt recovery with backup map?")) { |
| 249 |
bile = bile_open_recover_map(path, vrefnum); |
| 250 |
if (bile == NULL) |
| 251 |
panic("Failed to recover DB file %s: %d", |
| 252 |
PtoCstr(path), bile_error(NULL)); |
| 253 |
} else { |
| 254 |
panic("Failed to open DB file %s: %d", PtoCstr(path), |
| 255 |
bile_error(NULL)); |
| 256 |
} |
| 257 |
} |
| 258 |
} |
| 259 |
|
| 260 |
/* we got this far, store it as the last-accessed file */ |
| 261 |
if (vrefnum == 0) |
| 262 |
memcpy(&fullpath, path, sizeof(fullpath)); |
| 263 |
else |
| 264 |
getpath(vrefnum, path, fullpath, true); |
| 265 |
lastfileh = Get1Resource('STR ', STR_LAST_DB); |
| 266 |
if (lastfileh) |
| 267 |
xSetHandleSize(lastfileh, fullpath[0] + 1); |
| 268 |
else |
| 269 |
lastfileh = xNewHandle(fullpath[0] + 1); |
| 270 |
HLock(lastfileh); |
| 271 |
memcpy(*lastfileh, fullpath, fullpath[0] + 1); |
| 272 |
HUnlock(lastfileh); |
| 273 |
if (HomeResFile(lastfileh) == -1) |
| 274 |
AddResource(lastfileh, 'STR ', STR_LAST_DB, "\pSTR_LAST_DB"); |
| 275 |
else |
| 276 |
ChangedResource(lastfileh); |
| 277 |
WriteResource(lastfileh); |
| 278 |
DetachResource(lastfileh); |
| 279 |
|
| 280 |
tdb = xmalloczero(sizeof(struct db)); |
| 281 |
if (tdb == NULL) |
| 282 |
panic("Can't create db"); |
| 283 |
tdb->bile = bile; |
| 284 |
|
| 285 |
/* create views directory if it doesn't exist */ |
| 286 |
if (getpath(tdb->bile->vrefnum, tdb->bile->filename, viewsdir, |
| 287 |
false) != 0) |
| 288 |
panic("getpath failed on %s", PtoCstr(tdb->bile->filename)); |
| 289 |
PtoCstr(viewsdir); |
| 290 |
strlcat((char *)viewsdir, ":views", sizeof(Str255)); |
| 291 |
CtoPstr(viewsdir); |
| 292 |
if (!FIsDir(viewsdir)) { |
| 293 |
error = DirCreate(tdb->bile->vrefnum, 0, viewsdir, &dirid); |
| 294 |
if (error) |
| 295 |
panic("Failed creating %s: %d", PtoCstr(viewsdir), error); |
| 296 |
} |
| 297 |
|
| 298 |
if (db_migrate(tdb, was_new, fullpath) != 0) { |
| 299 |
bile_close(tdb->bile); |
| 300 |
xfree(&tdb); |
| 301 |
return NULL; |
| 302 |
} |
| 303 |
|
| 304 |
if (!was_new) |
| 305 |
db_config_load(tdb); |
| 306 |
|
| 307 |
memcpy(newfullpath, fullpath, sizeof(newfullpath)); |
| 308 |
PtoCstr(newfullpath); |
| 309 |
strlcat((char *)&newfullpath, "-sessions", sizeof(newfullpath)); |
| 310 |
CtoPstr(newfullpath); |
| 311 |
|
| 312 |
tdb->sessions_bile = bile_open(newfullpath, vrefnum); |
| 313 |
if (tdb->sessions_bile == NULL) { |
| 314 |
tdb->sessions_bile = bile_create(newfullpath, vrefnum, |
| 315 |
SUBTEXT_CREATOR, SL_TYPE); |
| 316 |
if (tdb->sessions_bile == NULL) |
| 317 |
panic("Couldn't create %s: %d", PtoCstr(newfullpath), |
| 318 |
bile_error(NULL)); |
| 319 |
} |
| 320 |
|
| 321 |
memcpy(newfullpath, fullpath, sizeof(newfullpath)); |
| 322 |
PtoCstr(newfullpath); |
| 323 |
strlcat((char *)&newfullpath, "-mail", sizeof(newfullpath)); |
| 324 |
CtoPstr(newfullpath); |
| 325 |
|
| 326 |
tdb->mail_bile = bile_open(newfullpath, vrefnum); |
| 327 |
if (tdb->mail_bile == NULL) { |
| 328 |
tdb->mail_bile = bile_create(newfullpath, vrefnum, |
| 329 |
SUBTEXT_CREATOR, MAIL_SPOOL_TYPE); |
| 330 |
if (tdb->mail_bile == NULL) |
| 331 |
panic("Couldn't create %s: %d", PtoCstr(newfullpath), |
| 332 |
bile_error(NULL)); |
| 333 |
} |
| 334 |
|
| 335 |
return tdb; |
| 336 |
} |
| 337 |
|
| 338 |
void |
| 339 |
db_close(struct db *tdb) |
| 340 |
{ |
| 341 |
short n; |
| 342 |
|
| 343 |
bile_close(tdb->bile); |
| 344 |
xfree(&tdb->bile); |
| 345 |
|
| 346 |
if (tdb->sessions_bile != NULL) { |
| 347 |
bile_close(tdb->sessions_bile); |
| 348 |
xfree(&tdb->sessions_bile); |
| 349 |
} |
| 350 |
|
| 351 |
if (tdb->mail_bile != NULL) { |
| 352 |
bile_close(tdb->mail_bile); |
| 353 |
xfree(&tdb->mail_bile); |
| 354 |
} |
| 355 |
|
| 356 |
if (tdb->boards) { |
| 357 |
for (n = 0; n < tdb->nboards; n++) { |
| 358 |
if (tdb->boards[n].bile) |
| 359 |
bile_close(tdb->boards[n].bile); |
| 360 |
} |
| 361 |
xfree(&tdb->boards); |
| 362 |
} |
| 363 |
|
| 364 |
if (tdb->folders) { |
| 365 |
for (n = 0; n < tdb->nfolders; n++) { |
| 366 |
if (tdb->folders[n].bile) |
| 367 |
bile_close(tdb->folders[n].bile); |
| 368 |
} |
| 369 |
xfree(&tdb->folders); |
| 370 |
} |
| 371 |
|
| 372 |
xfree(&tdb); |
| 373 |
} |
| 374 |
|
| 375 |
short |
| 376 |
db_migrate(struct db *tdb, short is_new, Str255 fullpath) |
| 377 |
{ |
| 378 |
struct user *suser; |
| 379 |
struct db *olddb; |
| 380 |
char ver; |
| 381 |
|
| 382 |
if (is_new) { |
| 383 |
ver = DB_CUR_VERS; |
| 384 |
|
| 385 |
/* setup some defaults */ |
| 386 |
sprintf(tdb->config.name, "Example Subtext BBS"); |
| 387 |
sprintf(tdb->config.phone_number, "(555) 867-5309"); |
| 388 |
sprintf(tdb->config.location, "Springfield"); |
| 389 |
sprintf(tdb->config.hostname, "bbs.example.com"); |
| 390 |
sprintf(tdb->config.timezone, "CT"); |
| 391 |
tdb->config.modem_speed = 9600; |
| 392 |
sprintf(tdb->config.modem_init, "ATV1S0=2&D2"); |
| 393 |
sprintf(tdb->config.modem_parity, "8N1"); |
| 394 |
tdb->config.modem_rings = 1; |
| 395 |
tdb->config.max_idle_minutes = 5; |
| 396 |
tdb->config.max_sysop_idle_minutes = 60; |
| 397 |
tdb->config.max_login_seconds = 90; |
| 398 |
tdb->config.blanker_idle_seconds = (60 * 60); |
| 399 |
tdb->config.blanker_runtime_seconds = 30; |
| 400 |
tdb->config.session_log_prune_days = 21; |
| 401 |
tdb->config.binkp_port = 24554; |
| 402 |
tdb->config.binkp_interval_seconds = (60 * 60 * 3); |
| 403 |
tdb->config.mail_prune_days = 180; |
| 404 |
tdb->config.ftn_max_tossed_message_size = 16 * 1024; |
| 405 |
|
| 406 |
db_config_save(tdb); |
| 407 |
|
| 408 |
/* create a default sysop user */ |
| 409 |
suser = xmalloczero(sizeof(struct user)); |
| 410 |
if (suser == NULL) |
| 411 |
panic("Can't allocate new user"); |
| 412 |
strncpy(suser->username, "sysop", sizeof(suser->username)); |
| 413 |
suser->created_at = Time; |
| 414 |
suser->is_enabled = DB_TRUE; |
| 415 |
user_set_password(suser, "p4ssw0rd"); |
| 416 |
suser->is_sysop = DB_TRUE; |
| 417 |
/* user_save assumes db is already set */ |
| 418 |
olddb = db; |
| 419 |
db = tdb; |
| 420 |
user_save(suser); |
| 421 |
xfree(&suser); |
| 422 |
user_cache_usernames(); |
| 423 |
db = olddb; |
| 424 |
} else { |
| 425 |
if (bile_read(tdb->bile, DB_VERS_RTYPE, 1, &ver, 1) != 1) |
| 426 |
ver = 1; |
| 427 |
|
| 428 |
if (ver == DB_CUR_VERS) |
| 429 |
return 0; |
| 430 |
|
| 431 |
if (ask("Migrate this database from version %d to %d to open it?", |
| 432 |
ver, DB_CUR_VERS) != ASK_YES) |
| 433 |
return -1; |
| 434 |
} |
| 435 |
|
| 436 |
/* per-version migrations */ |
| 437 |
while (ver < DB_CUR_VERS) { |
| 438 |
progress("Migrating from version %d to %d...", (short)ver, |
| 439 |
(short)(ver + 1)); |
| 440 |
|
| 441 |
if (ver < 18) |
| 442 |
panic("This Subtext database is too old to upgrade. Please " |
| 443 |
"run an older Subtext release first to upgrade it."); |
| 444 |
|
| 445 |
switch (ver) { |
| 446 |
case 18: { |
| 447 |
/* 18->19, ipdb_path, add session_log.location */ |
| 448 |
struct config new_config = { 0 }; |
| 449 |
Str255 newfullpath; |
| 450 |
struct bile *sessions_bile; |
| 451 |
size_t nids, n, size; |
| 452 |
unsigned long *ids; |
| 453 |
char *data; |
| 454 |
|
| 455 |
bile_read(tdb->bile, DB_CONFIG_RTYPE, 1, (char *)&new_config, |
| 456 |
sizeof(new_config)); |
| 457 |
|
| 458 |
new_config.ipdb_path[0] = '\0'; |
| 459 |
|
| 460 |
bile_write(tdb->bile, DB_CONFIG_RTYPE, 1, &new_config, |
| 461 |
sizeof(new_config)); |
| 462 |
|
| 463 |
/* migrate session log entries */ |
| 464 |
memcpy(&newfullpath, fullpath, sizeof(newfullpath)); |
| 465 |
PtoCstr(newfullpath); |
| 466 |
strlcat((char *)&newfullpath, "-sessions", sizeof(newfullpath)); |
| 467 |
CtoPstr(newfullpath); |
| 468 |
|
| 469 |
sessions_bile = bile_open(newfullpath, tdb->bile->vrefnum); |
| 470 |
if (sessions_bile == NULL) |
| 471 |
/* nothing to migrate */ |
| 472 |
break; |
| 473 |
|
| 474 |
nids = bile_ids_by_type(sessions_bile, SL_LOG_RTYPE, &ids); |
| 475 |
for (n = 0; n < nids; n++) { |
| 476 |
size = bile_read_alloc(sessions_bile, SL_LOG_RTYPE, |
| 477 |
ids[n], &data); |
| 478 |
size += member_size(struct session_log, location); |
| 479 |
if (bile_resize(sessions_bile, SL_LOG_RTYPE, |
| 480 |
ids[n], size) != size) |
| 481 |
panic("failed resizing session log %ld", ids[n]); |
| 482 |
} |
| 483 |
|
| 484 |
bile_flush(sessions_bile, true); |
| 485 |
xfree(&ids); |
| 486 |
bile_close(sessions_bile); |
| 487 |
break; |
| 488 |
} |
| 489 |
case 19: { |
| 490 |
/* 19->20, modem rings */ |
| 491 |
struct config new_config = { 0 }; |
| 492 |
|
| 493 |
bile_read(tdb->bile, DB_CONFIG_RTYPE, 1, (char *)&new_config, |
| 494 |
sizeof(new_config)); |
| 495 |
|
| 496 |
new_config.modem_rings = 1; |
| 497 |
|
| 498 |
bile_write(tdb->bile, DB_CONFIG_RTYPE, 1, &new_config, |
| 499 |
sizeof(new_config)); |
| 500 |
break; |
| 501 |
} |
| 502 |
case 20: { |
| 503 |
/* 20->21, move views out of db */ |
| 504 |
size_t size; |
| 505 |
char *data; |
| 506 |
|
| 507 |
#define DB_TEXT_TYPE 'TEXT' |
| 508 |
#define DB_TEXT_MENU_ID 1 |
| 509 |
#define DB_TEXT_SHORTMENU_ID 2 |
| 510 |
#define DB_TEXT_ISSUE_ID 3 |
| 511 |
#define DB_TEXT_SIGNUP_ID 4 |
| 512 |
#define DB_TEXT_PAGE_SYSOP_ID 5 |
| 513 |
#define DB_TEXT_NO_FREE_NODES_ID 6 |
| 514 |
#define DB_TEXT_SIGNOFF_ID 7 |
| 515 |
#define DB_TEXT_MENU_OPTIONS_ID 8 |
| 516 |
|
| 517 |
progress("Migrating views out of database..."); |
| 518 |
|
| 519 |
if ((size = bile_read_alloc(tdb->bile, DB_TEXT_TYPE, |
| 520 |
DB_TEXT_MENU_ID, &data))) { |
| 521 |
db_view_write(tdb, DB_VIEW_MENU, data, size); |
| 522 |
xfree(&data); |
| 523 |
bile_delete(tdb->bile, DB_TEXT_TYPE, DB_TEXT_MENU_ID, 0); |
| 524 |
} |
| 525 |
if ((size = bile_read_alloc(tdb->bile, DB_TEXT_TYPE, |
| 526 |
DB_TEXT_SHORTMENU_ID, &data))) { |
| 527 |
db_view_write(tdb, DB_VIEW_SHORTMENU, data, size); |
| 528 |
xfree(&data); |
| 529 |
bile_delete(tdb->bile, DB_TEXT_TYPE, DB_TEXT_SHORTMENU_ID, |
| 530 |
0); |
| 531 |
} |
| 532 |
if ((size = bile_read_alloc(tdb->bile, DB_TEXT_TYPE, |
| 533 |
DB_TEXT_ISSUE_ID, &data))) { |
| 534 |
db_view_write(tdb, DB_VIEW_ISSUE, data, size); |
| 535 |
xfree(&data); |
| 536 |
bile_delete(tdb->bile, DB_TEXT_TYPE, DB_TEXT_ISSUE_ID, 0); |
| 537 |
} |
| 538 |
if ((size = bile_read_alloc(tdb->bile, DB_TEXT_TYPE, |
| 539 |
DB_TEXT_SIGNUP_ID, &data))) { |
| 540 |
db_view_write(tdb, DB_VIEW_SIGNUP, data, size); |
| 541 |
xfree(&data); |
| 542 |
bile_delete(tdb->bile, DB_TEXT_TYPE, DB_TEXT_SIGNUP_ID, 0); |
| 543 |
} |
| 544 |
if ((size = bile_read_alloc(tdb->bile, DB_TEXT_TYPE, |
| 545 |
DB_TEXT_PAGE_SYSOP_ID, &data))) { |
| 546 |
db_view_write(tdb, DB_VIEW_PAGE_SYSOP, data, size); |
| 547 |
xfree(&data); |
| 548 |
bile_delete(tdb->bile, DB_TEXT_TYPE, DB_TEXT_PAGE_SYSOP_ID, |
| 549 |
0); |
| 550 |
} |
| 551 |
if ((size = bile_read_alloc(tdb->bile, DB_TEXT_TYPE, |
| 552 |
DB_TEXT_NO_FREE_NODES_ID, &data))) { |
| 553 |
db_view_write(tdb, DB_VIEW_NO_FREE_NODES, data, size); |
| 554 |
xfree(&data); |
| 555 |
bile_delete(tdb->bile, DB_TEXT_TYPE, |
| 556 |
DB_TEXT_NO_FREE_NODES_ID, 0); |
| 557 |
} |
| 558 |
if ((size = bile_read_alloc(tdb->bile, DB_TEXT_TYPE, |
| 559 |
DB_TEXT_SIGNOFF_ID, &data))) { |
| 560 |
db_view_write(tdb, DB_VIEW_SIGNOFF, data, size); |
| 561 |
xfree(&data); |
| 562 |
bile_delete(tdb->bile, DB_TEXT_TYPE, DB_TEXT_SIGNOFF_ID, 0); |
| 563 |
} |
| 564 |
if ((size = bile_read_alloc(tdb->bile, DB_TEXT_TYPE, |
| 565 |
DB_TEXT_MENU_OPTIONS_ID, &data))) { |
| 566 |
db_view_write(tdb, DB_VIEW_MENU_OPTIONS, data, size); |
| 567 |
xfree(&data); |
| 568 |
bile_delete(tdb->bile, DB_TEXT_TYPE, |
| 569 |
DB_TEXT_MENU_OPTIONS_ID, 0); |
| 570 |
} |
| 571 |
|
| 572 |
break; |
| 573 |
} |
| 574 |
case 21: { |
| 575 |
/* 20->21, syslog ip */ |
| 576 |
struct config new_config = { 0 }; |
| 577 |
|
| 578 |
bile_read(tdb->bile, DB_CONFIG_RTYPE, 1, (char *)&new_config, |
| 579 |
sizeof(new_config)); |
| 580 |
|
| 581 |
new_config.syslog_ip = 0; |
| 582 |
|
| 583 |
bile_write(tdb->bile, DB_CONFIG_RTYPE, 1, &new_config, |
| 584 |
sizeof(new_config)); |
| 585 |
break; |
| 586 |
} |
| 587 |
} |
| 588 |
|
| 589 |
ver++; |
| 590 |
} |
| 591 |
|
| 592 |
progress(NULL); |
| 593 |
|
| 594 |
/* store new version */ |
| 595 |
ver = DB_CUR_VERS; |
| 596 |
if (bile_write(tdb->bile, DB_VERS_RTYPE, 1, &ver, 1) != 1) |
| 597 |
panic("Failed writing new version: %d", bile_error(tdb->bile)); |
| 598 |
|
| 599 |
return 0; |
| 600 |
} |
| 601 |
|
| 602 |
void |
| 603 |
db_config_save(struct db *tdb) |
| 604 |
{ |
| 605 |
if (bile_write(tdb->bile, DB_CONFIG_RTYPE, 1, &tdb->config, |
| 606 |
sizeof(tdb->config)) != sizeof(tdb->config)) |
| 607 |
panic("db_config_save: failed to write: %d", |
| 608 |
bile_error(tdb->bile)); |
| 609 |
} |
| 610 |
|
| 611 |
void |
| 612 |
db_config_load(struct db *tdb) |
| 613 |
{ |
| 614 |
size_t rlen; |
| 615 |
char *newconfig; |
| 616 |
|
| 617 |
rlen = bile_read_alloc(tdb->bile, DB_CONFIG_RTYPE, 1, &newconfig); |
| 618 |
if (rlen == 0 || bile_error(tdb->bile)) |
| 619 |
panic("db_config_load: error reading config: %d", |
| 620 |
bile_error(tdb->bile)); |
| 621 |
if (rlen != sizeof(tdb->config)) |
| 622 |
warn("db_config_load: read config of size %lu, but expected %lu", |
| 623 |
rlen, sizeof(tdb->config)); |
| 624 |
|
| 625 |
memcpy(&tdb->config, newconfig, sizeof(tdb->config)); |
| 626 |
xfree(&newconfig); |
| 627 |
} |
| 628 |
|
| 629 |
void |
| 630 |
db_cache_boards(struct db *tdb) |
| 631 |
{ |
| 632 |
Str255 db_filename, board_filename; |
| 633 |
struct board_id_time_map first_map; |
| 634 |
size_t n, size; |
| 635 |
unsigned long *ids; |
| 636 |
struct bile_object *obj; |
| 637 |
char *data = NULL; |
| 638 |
|
| 639 |
if (tdb->boards) { |
| 640 |
for (n = 0; n < tdb->nboards; n++) { |
| 641 |
if (tdb->boards[n].bile) |
| 642 |
bile_close(tdb->boards[n].bile); |
| 643 |
} |
| 644 |
xfree(&tdb->boards); |
| 645 |
} |
| 646 |
|
| 647 |
if (getpath(tdb->bile->vrefnum, tdb->bile->filename, db_filename, |
| 648 |
false) != 0) |
| 649 |
panic("getpath failed on %s", PtoCstr(tdb->bile->filename)); |
| 650 |
PtoCstr(db_filename); |
| 651 |
|
| 652 |
tdb->nboards = bile_sorted_ids_by_type(tdb->bile, DB_BOARD_RTYPE, &ids); |
| 653 |
if (!tdb->nboards) |
| 654 |
return; |
| 655 |
tdb->boards = xcalloc(tdb->nboards, sizeof(struct board)); |
| 656 |
if (tdb->boards == NULL) { |
| 657 |
tdb->nboards = 0; |
| 658 |
return; |
| 659 |
} |
| 660 |
|
| 661 |
/* |
| 662 |
* Read ids first so we have a consistent order, then try to open or |
| 663 |
* fix/recreate each bile, which may change their order in the map |
| 664 |
*/ |
| 665 |
for (n = 0; n < tdb->nboards; n++) { |
| 666 |
obj = bile_find(tdb->bile, DB_BOARD_RTYPE, ids[n]); |
| 667 |
if (obj == NULL) |
| 668 |
break; |
| 669 |
|
| 670 |
size = bile_read_alloc(tdb->bile, DB_BOARD_RTYPE, obj->id, &data); |
| 671 |
bile_unmarshall_object(tdb->bile, board_object_fields, |
| 672 |
nboard_object_fields, data, size, (char *)(&tdb->boards[n]), |
| 673 |
sizeof(struct board), true); |
| 674 |
xfree(&data); |
| 675 |
xfree(&obj); |
| 676 |
} |
| 677 |
|
| 678 |
xfree(&ids); |
| 679 |
|
| 680 |
for (n = 0; n < tdb->nboards; n++) { |
| 681 |
snprintf((char *)board_filename, sizeof(board_filename), |
| 682 |
"%s:%lu.%s", db_filename, tdb->boards[n].id, BOARD_FILENAME_EXT); |
| 683 |
CtoPstr(board_filename); |
| 684 |
tdb->boards[n].bile = bile_open(board_filename, |
| 685 |
tdb->bile->vrefnum); |
| 686 |
if (tdb->boards[n].bile != NULL) |
| 687 |
goto opened; |
| 688 |
|
| 689 |
PtoCstr(board_filename); |
| 690 |
|
| 691 |
if (bile_error(NULL) != fnfErr && |
| 692 |
ask("Attempt recovery of %s with backup map?", board_filename)) { |
| 693 |
CtoPstr(board_filename); |
| 694 |
tdb->boards[n].bile = bile_open_recover_map(board_filename, |
| 695 |
tdb->bile->vrefnum); |
| 696 |
if (tdb->boards[n].bile != NULL) |
| 697 |
continue; |
| 698 |
warn("Failed to recover board file %s: %d", |
| 699 |
PtoCstr(board_filename), bile_error(NULL)); |
| 700 |
} |
| 701 |
|
| 702 |
if (ask("Failed to open board bile %s (error %d), recreate it?", |
| 703 |
board_filename, bile_error(NULL)) == false) |
| 704 |
panic("Can't open board file, exiting"); |
| 705 |
|
| 706 |
tdb->boards[n].bile = db_board_create(tdb, &tdb->boards[n], true); |
| 707 |
if (tdb->boards[n].bile == NULL) |
| 708 |
panic("Failed to create board file %s: %d", |
| 709 |
board_filename, bile_error(NULL)); |
| 710 |
|
| 711 |
opened: |
| 712 |
size = bile_read(tdb->boards[n].bile, |
| 713 |
BOARD_SORTED_ID_MAP_RTYPE, 1, &first_map, sizeof(first_map)); |
| 714 |
if (size != sizeof(first_map)) { |
| 715 |
logger_printf("[db] Reindexing ids on board %s", |
| 716 |
tdb->boards[n].name); |
| 717 |
board_index_sorted_post_ids(&tdb->boards[n], NULL); |
| 718 |
size = bile_read(tdb->boards[n].bile, |
| 719 |
BOARD_SORTED_ID_MAP_RTYPE, 1, &first_map, sizeof(first_map)); |
| 720 |
if (size != sizeof(first_map)) { |
| 721 |
tdb->boards[n].last_post_at = 0; |
| 722 |
continue; |
| 723 |
} |
| 724 |
} |
| 725 |
tdb->boards[n].last_post_at = first_map.time; |
| 726 |
} |
| 727 |
} |
| 728 |
|
| 729 |
struct bile * |
| 730 |
db_board_create(struct db *tdb, struct board *board, bool delete_first) |
| 731 |
{ |
| 732 |
Str255 db_filename, board_filename; |
| 733 |
struct bile *board_bile; |
| 734 |
size_t size; |
| 735 |
short ret; |
| 736 |
char *data; |
| 737 |
|
| 738 |
ret = bile_marshall_object(tdb->bile, board_object_fields, |
| 739 |
nboard_object_fields, board, &data, &size); |
| 740 |
if (ret != 0 || size == 0) { |
| 741 |
warn("db_board_create: failed to marshall object"); |
| 742 |
return NULL; |
| 743 |
} |
| 744 |
|
| 745 |
if (bile_write(tdb->bile, DB_BOARD_RTYPE, board->id, data, |
| 746 |
size) != size) |
| 747 |
panic("save of new board failed: %d", bile_error(tdb->bile)); |
| 748 |
xfree(&data); |
| 749 |
|
| 750 |
if (getpath(tdb->bile->vrefnum, tdb->bile->filename, db_filename, |
| 751 |
false) != 0) |
| 752 |
panic("getpath failed on %s", PtoCstr(tdb->bile->filename)); |
| 753 |
PtoCstr(db_filename); |
| 754 |
|
| 755 |
snprintf((char *)&board_filename, sizeof(board_filename), "%s:%lu.%s", |
| 756 |
db_filename, board->id, BOARD_FILENAME_EXT); |
| 757 |
CtoPstr(board_filename); |
| 758 |
|
| 759 |
if (delete_first) |
| 760 |
FSDelete(board_filename, tdb->bile->vrefnum); |
| 761 |
|
| 762 |
board_bile = bile_create(board_filename, tdb->bile->vrefnum, |
| 763 |
SUBTEXT_CREATOR, DB_BOARD_RTYPE); |
| 764 |
if (board_bile == NULL) |
| 765 |
panic("Failed creating new board bile at %s: %d", |
| 766 |
PtoCstr(board_filename), bile_error(NULL)); |
| 767 |
|
| 768 |
return board_bile; |
| 769 |
} |
| 770 |
|
| 771 |
void |
| 772 |
db_board_delete(struct db *tdb, struct board *board) |
| 773 |
{ |
| 774 |
if (bile_delete(tdb->bile, DB_BOARD_RTYPE, board->id, 0) != 0) { |
| 775 |
warn("deletion of board %ld failed: %d", board->id, |
| 776 |
bile_error(tdb->bile)); |
| 777 |
return; |
| 778 |
} |
| 779 |
bile_close(board->bile); |
| 780 |
} |
| 781 |
|
| 782 |
void |
| 783 |
db_cache_folders(struct db *tdb) |
| 784 |
{ |
| 785 |
Str255 db_filename, folder_filename, folder_dir; |
| 786 |
size_t n, size; |
| 787 |
unsigned long *ids, id; |
| 788 |
struct bile_object *obj; |
| 789 |
char *data = NULL; |
| 790 |
short error; |
| 791 |
|
| 792 |
if (tdb->folders) { |
| 793 |
for (n = 0; n < tdb->nfolders; n++) { |
| 794 |
if (tdb->folders[n].bile) |
| 795 |
bile_close(tdb->folders[n].bile); |
| 796 |
} |
| 797 |
xfree(&tdb->folders); |
| 798 |
} |
| 799 |
|
| 800 |
if (getpath(tdb->bile->vrefnum, tdb->bile->filename, db_filename, |
| 801 |
false) != 0) |
| 802 |
panic("getpath failed on %s", PtoCstr(tdb->bile->filename)); |
| 803 |
PtoCstr(db_filename); |
| 804 |
|
| 805 |
tdb->nfolders = bile_sorted_ids_by_type(tdb->bile, DB_FOLDER_RTYPE, |
| 806 |
&ids); |
| 807 |
if (!tdb->nfolders) |
| 808 |
return; |
| 809 |
tdb->folders = xcalloc(tdb->nfolders, sizeof(struct folder)); |
| 810 |
if (tdb->folders == NULL) { |
| 811 |
tdb->nfolders = 0; |
| 812 |
return; |
| 813 |
} |
| 814 |
|
| 815 |
/* |
| 816 |
* Read ids first so we have a consistent order, then try to open or |
| 817 |
* fix/recreate each bile, which may change their order in the map |
| 818 |
*/ |
| 819 |
for (n = 0; n < tdb->nfolders; n++) { |
| 820 |
obj = bile_find(tdb->bile, DB_FOLDER_RTYPE, ids[n]); |
| 821 |
if (obj == NULL) |
| 822 |
break; |
| 823 |
|
| 824 |
size = bile_read_alloc(tdb->bile, DB_FOLDER_RTYPE, obj->id, &data); |
| 825 |
if (size == 0 || data == NULL) |
| 826 |
break; |
| 827 |
bile_unmarshall_object(tdb->bile, folder_object_fields, |
| 828 |
nfolder_object_fields, data, size, (char *)(&tdb->folders[n]), |
| 829 |
sizeof(struct folder), true); |
| 830 |
xfree(&data); |
| 831 |
} |
| 832 |
|
| 833 |
xfree(&ids); |
| 834 |
|
| 835 |
for (n = 0; n < tdb->nfolders; n++) { |
| 836 |
snprintf((char *)folder_filename, sizeof(folder_filename), |
| 837 |
"%s:%lu.%s", db_filename, tdb->folders[n].id, |
| 838 |
FOLDER_FILENAME_EXT); |
| 839 |
CtoPstr(folder_filename); |
| 840 |
tdb->folders[n].bile = bile_open(folder_filename, |
| 841 |
tdb->bile->vrefnum); |
| 842 |
if (tdb->folders[n].bile != NULL) |
| 843 |
continue; |
| 844 |
|
| 845 |
PtoCstr(folder_filename); |
| 846 |
|
| 847 |
if (bile_error(NULL) != fnfErr && |
| 848 |
ask("Attempt recovery of %s with backup map?", folder_filename)) { |
| 849 |
CtoPstr(folder_filename); |
| 850 |
tdb->folders[n].bile = bile_open_recover_map(folder_filename, |
| 851 |
tdb->bile->vrefnum); |
| 852 |
if (tdb->folders[n].bile != NULL) |
| 853 |
continue; |
| 854 |
warn("Failed to recover folder bile %s: %d", |
| 855 |
PtoCstr(folder_filename), bile_error(NULL)); |
| 856 |
} |
| 857 |
|
| 858 |
if (ask("Failed to open folder bile %s (error %d), recreate it?", |
| 859 |
folder_filename, bile_error(NULL)) == false) |
| 860 |
exit(0); |
| 861 |
|
| 862 |
tdb->folders[n].bile = db_folder_create(tdb, &tdb->folders[n], |
| 863 |
true); |
| 864 |
if (tdb->folders[n].bile == NULL) |
| 865 |
panic("Failed to create folder bile %s: %d", |
| 866 |
folder_filename, bile_error(NULL)); |
| 867 |
} |
| 868 |
|
| 869 |
for (n = 0; n < tdb->nfolders; n++) { |
| 870 |
/* make sure directory exists */ |
| 871 |
memcpy(folder_dir, tdb->folders[n].path, sizeof(folder_dir)); |
| 872 |
CtoPstr(folder_dir); |
| 873 |
if (!FIsDir(folder_dir) && |
| 874 |
ask("Folder %ld path \"%s\" does not exist, create it?", |
| 875 |
tdb->folders[n].id, tdb->folders[n].path)) { |
| 876 |
error = DirCreate(db->bile->vrefnum, 0, folder_dir, &id); |
| 877 |
if (error) |
| 878 |
warn("Failed creating %s: %d", tdb->folders[n].path, |
| 879 |
error); |
| 880 |
} |
| 881 |
} |
| 882 |
} |
| 883 |
|
| 884 |
struct bile * |
| 885 |
db_folder_create(struct db *tdb, struct folder *folder, bool delete_first) |
| 886 |
{ |
| 887 |
Str255 db_filename, folder_filename, folder_dir; |
| 888 |
struct bile *folder_bile; |
| 889 |
size_t size; |
| 890 |
short ret, newid, error; |
| 891 |
char *data; |
| 892 |
|
| 893 |
ret = bile_marshall_object(tdb->bile, folder_object_fields, |
| 894 |
nfolder_object_fields, folder, &data, &size); |
| 895 |
if (ret != 0 || size == 0) { |
| 896 |
warn("db_folder_create: failed to marshall object"); |
| 897 |
return NULL; |
| 898 |
} |
| 899 |
|
| 900 |
if (bile_write(tdb->bile, DB_FOLDER_RTYPE, folder->id, data, |
| 901 |
size) != size) |
| 902 |
panic("save of new folder failed: %d", bile_error(tdb->bile)); |
| 903 |
xfree(&data); |
| 904 |
|
| 905 |
if (getpath(tdb->bile->vrefnum, tdb->bile->filename, db_filename, |
| 906 |
false) != 0) |
| 907 |
panic("getpath failed on %s", PtoCstr(tdb->bile->filename)); |
| 908 |
PtoCstr(db_filename); |
| 909 |
|
| 910 |
snprintf((char *)&folder_filename, sizeof(folder_filename), |
| 911 |
"%s:%lu.%s", db_filename, folder->id, FOLDER_FILENAME_EXT); |
| 912 |
CtoPstr(folder_filename); |
| 913 |
|
| 914 |
if (delete_first) |
| 915 |
FSDelete(folder_filename, tdb->bile->vrefnum); |
| 916 |
|
| 917 |
folder_bile = bile_create(folder_filename, tdb->bile->vrefnum, |
| 918 |
SUBTEXT_CREATOR, DB_FOLDER_RTYPE); |
| 919 |
if (folder_bile == NULL) |
| 920 |
panic("Failed creating new folder bile at %s: %d", |
| 921 |
PtoCstr(folder_filename), bile_error(NULL)); |
| 922 |
|
| 923 |
memcpy(&folder_dir, folder->path, sizeof(folder_dir)); |
| 924 |
CtoPstr(folder_dir); |
| 925 |
if (!FIsDir(folder_dir)) { |
| 926 |
error = DirCreate(tdb->bile->vrefnum, 0, folder_dir, &newid); |
| 927 |
if (error) |
| 928 |
warn("Failed creating %s: %d", folder->path, error); |
| 929 |
} |
| 930 |
|
| 931 |
return folder_bile; |
| 932 |
} |
| 933 |
|
| 934 |
void |
| 935 |
db_folder_delete(struct db *tdb, struct folder *folder) |
| 936 |
{ |
| 937 |
if (bile_delete(tdb->bile, DB_FOLDER_RTYPE, folder->id, 0) != 0) { |
| 938 |
warn("deletion of folder %ld failed: %d", folder->id, |
| 939 |
bile_error(tdb->bile)); |
| 940 |
return; |
| 941 |
} |
| 942 |
bile_close(folder->bile); |
| 943 |
} |
| 944 |
|
| 945 |
const char * |
| 946 |
db_view_filename(short id) |
| 947 |
{ |
| 948 |
switch (id) { |
| 949 |
case DB_VIEW_MENU: |
| 950 |
return "menu.txt"; |
| 951 |
case DB_VIEW_SHORTMENU: |
| 952 |
return "short_menu.txt"; |
| 953 |
case DB_VIEW_ISSUE: |
| 954 |
return "issue.txt"; |
| 955 |
case DB_VIEW_SIGNUP: |
| 956 |
return "signup.txt"; |
| 957 |
case DB_VIEW_PAGE_SYSOP: |
| 958 |
return "page_sysop.txt"; |
| 959 |
case DB_VIEW_NO_FREE_NODES: |
| 960 |
return "no_free_nodes.txt"; |
| 961 |
case DB_VIEW_SIGNOFF: |
| 962 |
return "signoff.txt"; |
| 963 |
case DB_VIEW_MENU_OPTIONS: |
| 964 |
return "menu_options.txt"; |
| 965 |
default: |
| 966 |
panic("db_view_filename: unknown id %d", id); |
| 967 |
} |
| 968 |
} |
| 969 |
|
| 970 |
void |
| 971 |
db_cache_views(struct db *tdb) |
| 972 |
{ |
| 973 |
Str255 viewdir, viewpath; |
| 974 |
struct stat sb; |
| 975 |
struct main_menu_option *opts; |
| 976 |
Handle default_menu; |
| 977 |
short n; |
| 978 |
size_t size; |
| 979 |
short error, frefnum; |
| 980 |
|
| 981 |
logger_printf("[db] Caching views"); |
| 982 |
|
| 983 |
if (getpath(tdb->bile->vrefnum, tdb->bile->filename, viewdir, |
| 984 |
false) != 0) |
| 985 |
panic("getpath failed on %s", PtoCstr(tdb->bile->filename)); |
| 986 |
PtoCstr(viewdir); |
| 987 |
strlcat((char *)viewdir, ":views:", sizeof(Str255)); |
| 988 |
|
| 989 |
for (n = 0; n < DB_VIEW_COUNT; n++) { |
| 990 |
if (tdb->views[n]) |
| 991 |
xfree(&tdb->views[n]); |
| 992 |
tdb->views[n] = NULL; |
| 993 |
|
| 994 |
strlcpy((char *)viewpath, (char *)viewdir, sizeof(Str255)); |
| 995 |
strlcat((char *)viewpath, db_view_filename(n), sizeof(Str255)); |
| 996 |
CtoPstr(viewpath); |
| 997 |
|
| 998 |
if (FStat(viewpath, &sb) != 0) { |
| 999 |
if (n == DB_VIEW_MENU_OPTIONS) { |
| 1000 |
default_menu = GetResource('TEXT', MENU_DEFAULTS_ID); |
| 1001 |
if (default_menu == NULL) |
| 1002 |
panic("Failed to find TEXT resource %d for menu options", |
| 1003 |
MENU_DEFAULTS_ID); |
| 1004 |
size = GetHandleSize(default_menu); |
| 1005 |
HLock(default_menu); |
| 1006 |
db_view_write(tdb, n, *default_menu, size); |
| 1007 |
HUnlock(default_menu); |
| 1008 |
ReleaseResource(default_menu); |
| 1009 |
FStat(viewpath, &sb); |
| 1010 |
} else { |
| 1011 |
/* create a blank file so the sysop knows what to edit */ |
| 1012 |
db_view_write(tdb, n, "", 0); |
| 1013 |
|
| 1014 |
/* but leave the view NULL */ |
| 1015 |
continue; |
| 1016 |
} |
| 1017 |
} |
| 1018 |
|
| 1019 |
if (sb.st_size == 0) |
| 1020 |
continue; |
| 1021 |
|
| 1022 |
tdb->views[n] = xmalloc(sb.st_size + 1); |
| 1023 |
if (tdb->views[n] == NULL) |
| 1024 |
panic("Failed allocating %ld for view %s", |
| 1025 |
sb.st_size, PtoCstr(viewpath)); |
| 1026 |
|
| 1027 |
error = FSOpen(viewpath, 0, &frefnum); |
| 1028 |
if (error) |
| 1029 |
panic("Error opening view %s: %d", PtoCstr(viewpath), error); |
| 1030 |
|
| 1031 |
size = sb.st_size; |
| 1032 |
error = FSRead(frefnum, &size, tdb->views[n]); |
| 1033 |
FSClose(frefnum); |
| 1034 |
|
| 1035 |
if (error && error != eofErr) |
| 1036 |
panic("Error reading view %s: %d", PtoCstr(viewpath), |
| 1037 |
error); |
| 1038 |
|
| 1039 |
if (size > sb.st_size) |
| 1040 |
panic("FSRead read more (%ld) than file size (%ld) for %s", |
| 1041 |
size, sb.st_size, PtoCstr(viewpath)); |
| 1042 |
|
| 1043 |
tdb->views[n][size] = '\0'; |
| 1044 |
|
| 1045 |
if (n == DB_VIEW_MENU_OPTIONS) { |
| 1046 |
opts = main_menu_parse(tdb->views[n], sb.st_size); |
| 1047 |
if (opts) { |
| 1048 |
if (main_menu_options != NULL) |
| 1049 |
xfree(&main_menu_options); |
| 1050 |
main_menu_options = opts; |
| 1051 |
} |
| 1052 |
} |
| 1053 |
} |
| 1054 |
} |
| 1055 |
|
| 1056 |
void |
| 1057 |
db_view_write(struct db *tdb, short id, char *str, size_t size) |
| 1058 |
{ |
| 1059 |
Str255 viewpath; |
| 1060 |
size_t len; |
| 1061 |
short error, frefnum; |
| 1062 |
|
| 1063 |
if (getpath(tdb->bile->vrefnum, tdb->bile->filename, viewpath, |
| 1064 |
false) != 0) |
| 1065 |
panic("getpath failed on %s", PtoCstr(tdb->bile->filename)); |
| 1066 |
PtoCstr(viewpath); |
| 1067 |
strlcat((char *)viewpath, ":views:", sizeof(Str255)); |
| 1068 |
strlcat((char *)viewpath, db_view_filename(id), sizeof(Str255)); |
| 1069 |
logger_printf("[db] Writing default view %s", viewpath); |
| 1070 |
CtoPstr(viewpath); |
| 1071 |
|
| 1072 |
error = Create(viewpath, 0, 'ttxt', 'TEXT'); |
| 1073 |
if (error == dupFNErr) |
| 1074 |
panic("View file already exists: %s", PtoCstr(viewpath)); |
| 1075 |
if (error) |
| 1076 |
panic("Failed creating %s: %d", PtoCstr(viewpath), error); |
| 1077 |
|
| 1078 |
if ((error = FSOpen(viewpath, 0, &frefnum))) |
| 1079 |
panic("Failed opening newly-created %s: %d", PtoCstr(viewpath), |
| 1080 |
error); |
| 1081 |
|
| 1082 |
len = size; |
| 1083 |
if ((error = Allocate(frefnum, &len))) |
| 1084 |
panic("Failed setting %s to size %ld: %d", PtoCstr(viewpath), |
| 1085 |
size, error); |
| 1086 |
|
| 1087 |
len = size; |
| 1088 |
if ((error = FSWrite(frefnum, &len, str))) |
| 1089 |
panic("Failed writing view to file %s: %d", PtoCstr(viewpath), |
| 1090 |
error); |
| 1091 |
if (len != size) |
| 1092 |
panic("Short write of view to file %s: %ld != %ld", |
| 1093 |
PtoCstr(viewpath), len, size); |
| 1094 |
|
| 1095 |
FSClose(frefnum); |
| 1096 |
} |