]> code.citadel.org Git - citadel.git/blob - citadel/citadel_ipc.c
* citadel_ipc.c: Fix for segfault on empty messages
[citadel.git] / citadel / citadel_ipc.c
1 /* $Id$ */
2
3 #include "sysdep.h"
4 #if TIME_WITH_SYS_TIME
5 # include <sys/time.h>
6 # include <time.h>
7 #else
8 # if HAVE_SYS_TIME_H
9 #  include <sys/time.h>
10 # else
11 #  include <time.h>
12 # endif
13 #endif
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <string.h>
17 #include <malloc.h>
18 #include <stdlib.h>
19 #include <ctype.h>
20 #ifdef THREADED_CLIENT
21 #include <pthread.h>
22 #endif
23 #include "citadel.h"
24 #include "citadel_ipc.h"
25 #include "client_crypto.h"
26 #include "tools.h"
27
28 #ifdef THREADED_CLIENT
29 pthread_mutex_t rwlock;
30 #endif
31 char express_msgs = 0;
32
33 static volatile int download_in_progress = 0;   /* download file open */
34 static volatile int upload_in_progress = 0;     /* upload file open */
35 /* static volatile int serv_sock; */    /* Socket on which we talk to server */
36
37
38 /*
39  * Does nothing.  The server should always return 200.
40  */
41 int CtdlIPCNoop(void)
42 {
43         char aaa[128];
44
45         return CtdlIPCGenericCommand("NOOP", NULL, 0, NULL, NULL, aaa);
46 }
47
48
49 /*
50  * Does nothing interesting.  The server should always return 200
51  * along with your string.
52  */
53 int CtdlIPCEcho(const char *arg, char *cret)
54 {
55         register int ret;
56         char *aaa;
57         
58         if (!arg) return -2;
59         if (!cret) return -2;
60
61         aaa = (char *)malloc(strlen(arg) + 6);
62         if (!aaa) return -1;
63
64         sprintf(aaa, "ECHO %s", arg);
65         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
66         free(aaa);
67         return ret;
68 }
69
70
71 /*
72  * Asks the server to close the connecction.
73  * Should always return 200.
74  */
75 int CtdlIPCQuit(void)
76 {
77         register int ret;
78         char aaa[128];
79
80         netio_lock();
81         serv_puts("QUIT");
82         serv_gets(aaa);
83         ret = atoi(aaa);
84         netio_unlock();
85         return ret;
86 }
87
88
89 /*
90  * Asks the server to logout.  Should always return 200, even if no user
91  * was logged in.  The user will not be logged in after this!
92  */
93 int CtdlIPCLogout(void)
94 {
95         register int ret;
96         char aaa[128];
97
98         netio_lock();
99         serv_puts("LOUT");
100         serv_gets(aaa);
101         ret = atoi(aaa);
102         netio_unlock();
103         return ret;
104 }
105
106
107 /*
108  * First stage of authentication - pass the username.  Returns 300 if the
109  * username is able to log in, with the username correctly spelled in cret.
110  * Returns various 500 error codes if the user doesn't exist, etc.
111  */
112 int CtdlIPCTryLogin(const char *username, char *cret)
113 {
114         register int ret;
115         char *aaa;
116
117         if (!username) return -2;
118         if (!cret) return -2;
119
120         aaa = (char *)malloc(strlen(username) + 6);
121         if (!aaa) return -1;
122
123         sprintf(aaa, "USER %s", username);
124         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
125         free(aaa);
126         return ret;
127 }
128
129
130 /*
131  * Second stage of authentication - provide password.  The server returns
132  * 200 and several arguments in cret relating to the user's account.
133  */
134 int CtdlIPCTryPassword(const char *passwd, char *cret)
135 {
136         register int ret;
137         char *aaa;
138
139         if (!passwd) return -2;
140         if (!cret) return -2;
141
142         aaa = (char *)malloc(strlen(passwd) + 6);
143         if (!aaa) return -1;
144
145         sprintf(aaa, "PASS %s", passwd);
146         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
147         free(aaa);
148         return ret;
149 }
150
151
152 /*
153  * Create a new user.  This returns 200 plus the same arguments as TryPassword
154  * if selfservice is nonzero, unless there was a problem creating the account.
155  * If selfservice is zero, creates a new user but does not log out the existing
156  * user - intended for use by system administrators to create accounts on
157  * behalf of other users.
158  */
159 int CtdlIPCCreateUser(const char *username, int selfservice, char *cret)
160 {
161         register int ret;
162         char *aaa;
163
164         if (!username) return -2;
165         if (!cret) return -2;
166
167         aaa = (char *)malloc(strlen(username) + 6);
168         if (!aaa) return -1;
169
170         sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
171         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
172         free(aaa);
173         return ret;
174 }
175
176
177 /*
178  * Changes the user's password.  Returns 200 if changed, errors otherwise.
179  */
180 int CtdlIPCChangePassword(const char *passwd, char *cret)
181 {
182         register int ret;
183         char *aaa;
184
185         if (!passwd) return -2;
186         if (!cret) return -2;
187
188         aaa = (char *)malloc(strlen(passwd) + 6);
189         if (!aaa) return -1;
190
191         sprintf(aaa, "SETP %s", passwd);
192         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
193         free(aaa);
194         return ret;
195 }
196
197
198 /* LKRN */
199 /* Caller must free the march list */
200 /* which is 0 = LRMS, 1 = LKRN, 2 = LKRO, 3 = LKRA, 4 = LZRM */
201 /* floor is -1 for all, or floornum */
202 int CtdlIPCKnownRooms(int which, int floor, struct march **listing, char *cret)
203 {
204         register int ret;
205         struct march *march = NULL;
206         static char *proto[] = {"LRMS", "LKRN", "LKRO", "LKRA", "LZRM" };
207         char aaa[SIZ];
208         char *bbb = NULL;
209         size_t bbbsize;
210
211         if (!listing) return -2;
212         if (*listing) return -2;        /* Free the listing first */
213         if (!cret) return -2;
214         if (which < 0 || which > 4) return -2;
215         if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
216
217         sprintf(aaa, "%s %d", proto[which], floor);
218         ret = CtdlIPCGenericCommand(aaa, NULL, 0, &bbb, &bbbsize, cret);
219         if (ret / 100 == 1) {
220                 struct march *mptr;
221
222                 while (bbb && strlen(bbb)) {
223                         int a;
224
225                         extract_token(aaa, bbb, 0, '\n');
226                         a = strlen(aaa);
227                         memmove(bbb, bbb + a + 1, strlen(bbb) - a);
228                         mptr = (struct march *) malloc(sizeof (struct march));
229                         if (mptr) {
230                                 mptr->next = NULL;
231                                 extract(mptr->march_name, aaa, 0);
232                                 mptr->march_floor = (char) extract_int(aaa, 2);
233                                 mptr->march_order = (char) extract_int(aaa, 3);
234                                 if (march == NULL)
235                                         march = mptr;
236                                 else {
237                                         struct march *mptr2;
238
239                                         mptr2 = march;
240                                         while (mptr2->next != NULL)
241                                                 mptr2 = mptr2->next;
242                                         mptr2->next = mptr;
243                                 }
244                         }
245                 }
246         }
247         *listing = march;
248         return ret;
249 }
250
251
252 /* GETU */
253 /* Caller must free the struct usersupp; caller may pass an existing one */
254 int CtdlIPCGetConfig(struct usersupp **uret, char *cret)
255 {
256         register int ret;
257
258         if (!cret) return -2;
259         if (!uret) return -2;
260         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof (struct usersupp));
261         if (!*uret) return -1;
262
263         ret = CtdlIPCGenericCommand("GETU", NULL, 0, NULL, NULL, cret);
264         if (ret / 100 == 2) {
265                 uret[0]->USscreenwidth = extract_int(cret, 0);
266                 uret[0]->USscreenheight = extract_int(cret, 1);
267                 uret[0]->flags = extract_int(cret, 2);
268         }
269         return ret;
270 }
271
272
273 /* SETU */
274 int CtdlIPCSetConfig(struct usersupp *uret, char *cret)
275 {
276         char aaa[48];
277
278         if (!uret) return -2;
279         if (!cret) return -2;
280
281         sprintf(aaa, "SETU %d|%d|%d",
282                         uret->USscreenwidth, uret->USscreenheight,
283                         uret->flags);
284         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
285 }
286
287
288 /* GOTO */
289 int CtdlIPCGotoRoom(const char *room, const char *passwd,
290                 struct ctdlipcroom **rret, char *cret)
291 {
292         register int ret;
293         char *aaa;
294
295         if (!cret) return -2;
296         if (!rret) return -2;
297         if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
298         if (!*rret) return -1;
299
300         if (passwd) {
301                 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
302                 if (!aaa) {
303                         free(*rret);
304                         return -1;
305                 }
306                 sprintf(aaa, "GOTO %s|%s", room, passwd);
307         } else {
308                 aaa = (char *)malloc(strlen(room) + 6);
309                 if (!aaa) {
310                         free(*rret);
311                         return -1;
312                 }
313                 sprintf(aaa, "GOTO %s", room);
314         }
315         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
316         if (ret / 100 == 2) {
317                 extract(rret[0]->RRname, cret, 0);
318                 rret[0]->RRunread = extract_long(cret, 1);
319                 rret[0]->RRtotal = extract_long(cret, 2);
320                 rret[0]->RRinfoupdated = extract_int(cret, 3);
321                 rret[0]->RRflags = extract_int(cret, 4);
322                 rret[0]->RRhighest = extract_long(cret, 5);
323                 rret[0]->RRlastread = extract_long(cret, 6);
324                 rret[0]->RRismailbox = extract_int(cret, 7);
325                 rret[0]->RRaide = extract_int(cret, 8);
326                 rret[0]->RRnewmail = extract_long(cret, 9);
327                 rret[0]->RRfloor = extract_int(cret, 10);
328         } else {
329                 free(*rret);
330         }
331         return ret;
332 }
333
334
335 /* MSGS */
336 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
337 /* whicharg is number of messages, applies to last, first, gt, lt */
338 int CtdlIPCGetMessages(int which, int whicharg, const char *template,
339                 long **mret, char *cret)
340 {
341         register int ret;
342         register long count = 0;
343         static char *proto[] =
344                 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
345         char aaa[33];
346         char *bbb;
347         size_t bbbsize;
348
349         if (!cret) return -2;
350         if (!mret) return -2;
351         if (*mret) return -2;
352         if (which < 0 || which > 6) return -2;
353
354         if (which <= 2)
355                 sprintf(aaa, "MSGS %s||%d", proto[which],
356                                 (template) ? 1 : 0);
357         else
358                 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
359                                 (template) ? 1 : 0);
360         if (template) count = strlen(template);
361         ret = CtdlIPCGenericCommand(aaa, template, count, &bbb, &bbbsize, cret);
362         count = 0;
363         while (strlen(bbb)) {
364                 int a;
365
366                 extract_token(aaa, bbb, 0, '\n');
367                 a = strlen(aaa);
368                 memmove(aaa, bbb + a + 1, strlen(bbb) - a - 1);
369                 *mret = (long *)realloc(mret, (count + 1) * sizeof (long));
370                 if (*mret)
371                         *mret[count++] = atol(aaa);
372                 *mret[count] = 0L;
373         }
374         return ret;
375 }
376
377
378 /* MSG0, MSG2 */
379 int CtdlIPCGetSingleMessage(long msgnum, int headers, int as_mime,
380                 struct ctdlipcmessage **mret, char *cret)
381 {
382         register int ret;
383         char aaa[SIZ];
384         char *bbb = NULL;
385         size_t bbbsize;
386
387         if (!cret) return -1;
388         if (!mret) return -1;
389         if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
390         if (!*mret) return -1;
391         if (!msgnum) return -1;
392
393         sprintf(aaa, "MSG%c %ld|%d", as_mime ? '2' : '0', msgnum, headers);
394         ret = CtdlIPCGenericCommand(aaa, NULL, 0, &bbb, &bbbsize, cret);
395         if (ret / 100 == 1) {
396                 if (!as_mime) {
397                         while (strlen(bbb) > 4 && bbb[4] == '=') {
398                                 extract_token(aaa, bbb, 0, '\n');
399                                 remove_token(bbb, 0, '\n');
400
401                                 if (!strncasecmp(aaa, "nhdr=yes", 8))
402                                         mret[0]->nhdr = 1;
403                                 else if (!strncasecmp(aaa, "from=", 5))
404                                         strcpy(mret[0]->author, &aaa[5]);
405                                 else if (!strncasecmp(aaa, "type=", 5))
406                                         mret[0]->type = atoi(&aaa[5]);
407                                 else if (!strncasecmp(aaa, "msgn=", 5))
408                                         strcpy(mret[0]->msgid, &aaa[5]);
409                                 else if (!strncasecmp(aaa, "subj=", 5))
410                                         strcpy(mret[0]->subject, &aaa[5]);
411                                 else if (!strncasecmp(aaa, "rfca=", 5))
412                                         strcpy(mret[0]->email, &aaa[5]);
413                                 else if (!strncasecmp(aaa, "hnod=", 5))
414                                         strcpy(mret[0]->hnod, &aaa[5]);
415                                 else if (!strncasecmp(aaa, "room=", 5))
416                                         strcpy(mret[0]->room, &aaa[5]);
417                                 else if (!strncasecmp(aaa, "node=", 5))
418                                         strcpy(mret[0]->node, &aaa[5]);
419                                 else if (!strncasecmp(aaa, "rcpt=", 5))
420                                         strcpy(mret[0]->recipient, &aaa[5]);
421                                 else if (!strncasecmp(aaa, "time=", 5))
422                                         mret[0]->time = atol(&aaa[5]);
423                                 else if (!strncasecmp(aaa, "part=", 5)) {
424                                         struct parts *ptr, *chain;
425         
426                                         ptr = (struct parts *)calloc(1, sizeof (struct parts));
427                                         if (ptr) {
428                                                 extract(ptr->name, &aaa[5], 0);
429                                                 extract(ptr->filename, &aaa[5], 1);
430                                                 extract(ptr->number, &aaa[5], 2);
431                                                 extract(ptr->disposition, &aaa[5], 3);
432                                                 extract(ptr->mimetype, &aaa[5], 4);
433                                                 ptr->length = extract_long(&aaa[5], 5);
434                                                 if (!mret[0]->attachments)
435                                                         mret[0]->attachments = ptr;
436                                                 else {
437                                                         chain = mret[0]->attachments;
438                                                         while (chain->next)
439                                                                 chain = chain->next;
440                                                         chain->next = ptr;
441                                                 }
442                                         }
443                                 }
444                         }
445                         /* Eliminate "text\n" */
446                         remove_token(bbb, 0, '\n');
447                 }
448                 if (strlen(bbb)) {
449                         /* Strip trailing whitespace */
450                         bbb = (char *)realloc(bbb, strlen(bbb) + 1);
451                 } else {
452                         bbb = (char *)realloc(bbb, 1);
453                         *bbb = '\0';
454                 }
455                 mret[0]->text = bbb;
456         }
457         return ret;
458 }
459
460
461 /* WHOK */
462 int CtdlIPCWhoKnowsRoom(char **listing, char *cret)
463 {
464         register int ret;
465         size_t bytes;
466
467         if (!cret) return -2;
468         if (!listing) return -2;
469         if (*listing) return -2;
470
471         ret = CtdlIPCGenericCommand("WHOK", NULL, 0, listing, &bytes, cret);
472         return ret;
473 }
474
475
476 /* INFO */
477 int CtdlIPCServerInfo(struct CtdlServInfo *ServInfo, char *cret)
478 {
479         register int ret;
480         size_t bytes;
481         char *listing = NULL;
482         char buf[SIZ];
483
484         if (!cret) return -2;
485         if (!ServInfo) return -2;
486
487         ret = CtdlIPCGenericCommand("INFO", NULL, 0, &listing, &bytes, cret);
488         if (ret / 100 == 1) {
489                 int line = 0;
490
491                 while (*listing && strlen(listing)) {
492                         extract_token(buf, listing, 0, '\n');
493                         remove_token(listing, 0, '\n');
494                         switch (line++) {
495                         case 0:         ServInfo->serv_pid = atoi(buf);
496                                         break;
497                         case 1:         strcpy(ServInfo->serv_nodename,buf);
498                                         break;
499                         case 2:         strcpy(ServInfo->serv_humannode,buf);
500                                         break;
501                         case 3:         strcpy(ServInfo->serv_fqdn,buf);
502                                         break;
503                         case 4:         strcpy(ServInfo->serv_software,buf);
504                                         break;
505                         case 5:         ServInfo->serv_rev_level = atoi(buf);
506                                         break;
507                         case 6:         strcpy(ServInfo->serv_bbs_city,buf);
508                                         break;
509                         case 7:         strcpy(ServInfo->serv_sysadm,buf);
510                                         break;
511                         case 9:         strcpy(ServInfo->serv_moreprompt,buf);
512                                         break;
513                         case 10:        ServInfo->serv_ok_floors = atoi(buf);
514                                         break;
515                         case 11:        ServInfo->serv_paging_level = atoi(buf);
516                                         break;
517                         case 13:        ServInfo->serv_supports_qnop = atoi(buf);
518                                         break;
519                         }
520                 }
521
522         }
523         return ret;
524 }
525
526
527 /* RDIR */
528 int CtdlIPCReadDirectory(char **listing, char *cret)
529 {
530         register int ret;
531         size_t bytes;
532
533         if (!cret) return -2;
534         if (!listing) return -2;
535         if (*listing) return -2;
536
537         ret = CtdlIPCGenericCommand("RDIR", NULL, 0, listing, &bytes, cret);
538         return ret;
539 }
540
541
542 /*
543  * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
544  */
545 int CtdlIPCSetLastRead(long msgnum, char *cret)
546 {
547         register int ret;
548         char aaa[16];
549
550         if (!cret) return -2;
551
552         if (msgnum)
553                 sprintf(aaa, "SLRP %ld", msgnum);
554         else
555                 sprintf(aaa, "SLRP HIGHEST");
556         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
557         return ret;
558 }
559
560
561 /* INVT */
562 int CtdlIPCInviteUserToRoom(const char *username, char *cret)
563 {
564         register int ret;
565         char *aaa;
566
567         if (!cret) return -2;
568         if (!username) return -2;
569
570         aaa = (char *)malloc(strlen(username) + 6);
571         if (!aaa) return -1;
572
573         sprintf(aaa, "INVT %s", username);
574         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
575         free(aaa);
576         return ret;
577 }
578
579
580 /* KICK */
581 int CtdlIPCKickoutUserFromRoom(const char *username, char *cret)
582 {
583         register int ret;
584         char *aaa;
585
586         if (!cret) return -1;
587         if (!username) return -1;
588
589         aaa = (char *)malloc(strlen(username) + 6);
590
591         sprintf(aaa, "KICK %s", username);
592         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
593         free(aaa);
594         return ret;
595 }
596
597
598 /* GETR */
599 int CtdlIPCGetRoomAttributes(struct quickroom **qret, char *cret)
600 {
601         register int ret;
602
603         if (!cret) return -2;
604         if (!qret) return -2;
605         if (!*qret) *qret = (struct quickroom *)calloc(1, sizeof (struct quickroom));
606         if (!*qret) return -1;
607
608         ret = CtdlIPCGenericCommand("GETR", NULL, 0, NULL, NULL, cret);
609         if (ret / 100 == 2) {
610                 extract(qret[0]->QRname, cret, 0);
611                 extract(qret[0]->QRpasswd, cret, 1);
612                 extract(qret[0]->QRdirname, cret, 2);
613                 qret[0]->QRflags = extract_int(cret, 3);
614                 qret[0]->QRfloor = extract_int(cret, 4);
615                 qret[0]->QRorder = extract_int(cret, 5);
616         }
617         return ret;
618 }
619
620
621 /* SETR */
622 /* set forget to kick all users out of room */
623 int CtdlIPCSetRoomAttributes(int forget, struct quickroom *qret, char *cret)
624 {
625         register int ret;
626         char *aaa;
627
628         if (!cret) return -2;
629         if (!qret) return -2;
630
631         aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
632                         strlen(qret->QRdirname) + 52);
633         if (!aaa) return -1;
634
635         sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d",
636                         qret->QRname, qret->QRpasswd, qret->QRdirname,
637                         qret->QRflags, forget, qret->QRfloor, qret->QRorder);
638         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
639         free(aaa);
640         return ret;
641 }
642
643
644 /* GETA */
645 int CtdlIPCGetRoomAide(char *cret)
646 {
647         if (!cret) return -1;
648
649         return CtdlIPCGenericCommand("GETA", NULL, 0, NULL, NULL, cret);
650 }
651
652
653 /* SETA */
654 int CtdlIPCSetRoomAide(const char *username, char *cret)
655 {
656         register int ret;
657         char *aaa;
658
659         if (!cret) return -2;
660         if (!username) return -2;
661
662         aaa = (char *)malloc(strlen(username) + 6);
663         if (!aaa) return -1;
664
665         sprintf(aaa, "SETA %s", username);
666         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
667         free(aaa);
668         return ret;
669 }
670
671
672 /* ENT0 */
673 int CtdlIPCPostMessage(int flag, const struct ctdlipcmessage *mr, char *cret)
674 {
675         register int ret;
676         char *aaa;
677
678         if (!cret) return -2;
679         if (!mr) return -2;
680
681         aaa = (char *)malloc(strlen(mr->recipient) + strlen(mr->author) + 40);
682         if (!aaa) return -1;
683
684         sprintf(aaa, "ENT0 %d|%s|%d|%d|%s", flag, mr->recipient, mr->anonymous,
685                         mr->type, mr->author);
686         ret = CtdlIPCGenericCommand(aaa, mr->text, strlen(mr->text), NULL,
687                         NULL, cret);
688         free(aaa);
689         return ret;
690 }
691
692
693 /* RINF */
694 int CtdlIPCRoomInfo(char **iret, char *cret)
695 {
696         size_t bytes;
697
698         if (!cret) return -2;
699         if (!iret) return -2;
700         if (*iret) return -2;
701
702         return CtdlIPCGenericCommand("RINF", NULL, 0, iret, &bytes, cret);
703 }
704
705
706 /* DELE */
707 int CtdlIPCDeleteMessage(long msgnum, char *cret)
708 {
709         char aaa[16];
710
711         if (!cret) return -2;
712         if (!msgnum) return -2;
713
714         sprintf(aaa, "DELE %ld", msgnum);
715         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
716 }
717
718
719 /* MOVE */
720 int CtdlIPCMoveMessage(int copy, long msgnum, const char *destroom, char *cret)
721 {
722         register int ret;
723         char *aaa;
724
725         if (!cret) return -2;
726         if (!destroom) return -2;
727         if (!msgnum) return -2;
728
729         aaa = (char *)malloc(strlen(destroom) + 28);
730         if (!aaa) return -1;
731
732         sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
733         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
734         free(aaa);
735         return ret;
736 }
737
738
739 /* KILL */
740 int CtdlIPCDeleteRoom(int for_real, char *cret)
741 {
742         char aaa[16];
743
744         if (!cret) return -2;
745
746         sprintf(aaa, "KILL %d", for_real);
747         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
748 }
749
750
751 /* CRE8 */
752 int CtdlIPCCreateRoom(int for_real, const char *roomname, int type,
753                 const char *password, int floor, char *cret)
754 {
755         register int ret;
756         char *aaa;
757
758         if (!cret) return -2;
759         if (!roomname) return -2;
760
761         if (password) {
762                 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
763                 if (!aaa) return -1;
764                 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
765                                 password, floor);
766         } else {
767                 aaa = (char *)malloc(strlen(roomname) + 40);
768                 if (!aaa) return -1;
769                 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
770                                 floor);
771         }
772         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
773         free(aaa);
774         return ret;
775 }
776
777
778 /* FORG */
779 int CtdlIPCForgetRoom(char *cret)
780 {
781         if (!cret) return -2;
782
783         return CtdlIPCGenericCommand("FORG", NULL, 0, NULL, NULL, cret);
784 }
785
786
787 /* MESG */
788 int CtdlIPCSystemMessage(const char *message, char **mret, char *cret)
789 {
790         register int ret;
791         char *aaa;
792         size_t bytes;
793
794         if (!cret) return -2;
795         if (!mret) return -2;
796         if (*mret) return -2;
797         if (!message) return -2;
798
799         aaa = (char *)malloc(strlen(message) + 6);
800         if (!aaa) return -1;
801
802         sprintf(aaa, "MESG %s", message);
803         ret = CtdlIPCGenericCommand(aaa, NULL, 0, mret, &bytes, cret);
804         free(aaa);
805         return ret;
806 }
807
808
809 /* GNUR */
810 int CtdlIPCNextUnvalidatedUser(char *cret)
811 {
812         if (!cret) return -2;
813
814         return CtdlIPCGenericCommand("GNUR", NULL, 0, NULL, NULL, cret);
815 }
816
817
818 /* GREG */
819 int CtdlIPCGetUserRegistration(const char *username, char **rret, char *cret)
820 {
821         register int ret;
822         char *aaa;
823         size_t bytes;
824
825         if (!cret) return -2;
826         if (!rret) return -2;
827         if (*rret) return -2;
828
829         if (username)
830                 aaa = (char *)malloc(strlen(username) + 6);
831         else
832                 aaa = (char *)malloc(12);
833         if (!aaa) return -1;
834
835         if (username)
836                 sprintf(aaa, "GREG %s", username);
837         else
838                 sprintf(aaa, "GREG _SELF_");
839         ret = CtdlIPCGenericCommand(aaa, NULL, 0, rret, &bytes, cret);
840         free(aaa);
841         return ret;
842 }
843
844
845 /* VALI */
846 int CtdlIPCValidateUser(const char *username, int axlevel, char *cret)
847 {
848         register int ret;
849         char *aaa;
850
851         if (!cret) return -2;
852         if (!username) return -2;
853         if (axlevel < 0 || axlevel > 7) return -2;
854
855         aaa = (char *)malloc(strlen(username) + 17);
856         if (!aaa) return -1;
857
858         sprintf(aaa, "VALI %s|%d", username, axlevel);
859         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
860         free(aaa);
861         return ret;
862 }
863
864
865 /* EINF */
866 int CtdlIPCSetRoomInfo(int for_real, const char *info, char *cret)
867 {
868         char aaa[16];
869
870         if (!cret) return -1;
871         if (!info) return -1;
872
873         sprintf(aaa, "EINF %d", for_real);
874         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
875 }
876
877
878 /* LIST */
879 int CtdlIPCUserListing(char **listing, char *cret)
880 {
881         size_t bytes;
882
883         if (!cret) return -1;
884         if (!listing) return -1;
885         if (*listing) return -1;
886
887         return CtdlIPCGenericCommand("LIST", NULL, 0, listing, &bytes, cret);
888 }
889
890
891 /* REGI */
892 int CtdlIPCSetRegistration(const char *info, char *cret)
893 {
894         if (!cret) return -1;
895         if (!info) return -1;
896
897         return CtdlIPCGenericCommand("REGI", info, strlen(info),
898                         NULL, NULL, cret);
899 }
900
901
902 /* CHEK */
903 int CtdlIPCMiscCheck(struct ctdlipcmisc *chek, char *cret)
904 {
905         register int ret;
906
907         if (!cret) return -1;
908         if (!chek) return -1;
909
910         ret = CtdlIPCGenericCommand("CHEK", NULL, 0, NULL, NULL, cret);
911         if (ret / 100 == 2) {
912                 chek->newmail = extract_long(cret, 0);
913                 chek->needregis = extract_int(cret, 1);
914                 chek->needvalid = extract_int(cret, 2);
915         }
916         return ret;
917 }
918
919
920 /* DELF */
921 int CtdlIPCDeleteFile(const char *filename, char *cret)
922 {
923         register int ret;
924         char *aaa;
925
926         if (!cret) return -2;
927         if (!filename) return -2;
928         
929         aaa = (char *)malloc(strlen(filename) + 6);
930         if (!aaa) return -1;
931
932         sprintf(aaa, "DELF %s", filename);
933         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
934         free(aaa);
935         return ret;
936 }
937
938
939 /* MOVF */
940 int CtdlIPCMoveFile(const char *filename, const char *destroom, char *cret)
941 {
942         register int ret;
943         char *aaa;
944
945         if (!cret) return -2;
946         if (!filename) return -2;
947         if (!destroom) return -2;
948
949         aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
950         if (!aaa) return -1;
951
952         sprintf(aaa, "MOVF %s|%s", filename, destroom);
953         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
954         free(aaa);
955         return ret;
956 }
957
958
959 /* NETF */
960 int CtdlIPCNetSendFile(const char *filename, const char *destnode, char *cret)
961 {
962         register int ret;
963         char *aaa;
964
965         if (!cret) return -2;
966         if (!filename) return -2;
967         if (!destnode) return -2;
968
969         aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
970         if (!aaa) return -1;
971
972         sprintf(aaa, "NETF %s|%s", filename, destnode);
973         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
974         free(aaa);
975         return ret;
976 }
977
978
979 /* RWHO */
980 int CtdlIPCOnlineUsers(char **listing, time_t *stamp, char *cret)
981 {
982         register int ret;
983         size_t bytes;
984
985         if (!cret) return -1;
986         if (!listing) return -1;
987         if (*listing) return -1;
988
989         *stamp = CtdlIPCServerTime(cret);
990         if (!*stamp)
991                 *stamp = time(NULL);
992         ret = CtdlIPCGenericCommand("RWHO", NULL, 0, listing, &bytes, cret);
993         return ret;
994 }
995
996
997 /* OPEN */
998 int CtdlIPCFileDownload(const char *filename, void **buf, char *cret)
999 {
1000         register int ret;
1001         size_t bytes;
1002         time_t last_mod;
1003         char mimetype[SIZ];
1004         char *aaa;
1005
1006         if (!cret) return -2;
1007         if (!filename) return -2;
1008         if (!buf) return -2;
1009         if (*buf) return -2;
1010         if (download_in_progress) return -2;
1011
1012         aaa = (char *)malloc(strlen(filename) + 6);
1013         if (!aaa) return -1;
1014
1015         sprintf(aaa, "OPEN %s", filename);
1016         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1017         free(aaa);
1018         /* FIXME: Possible race condition */
1019         if (ret / 100 == 2) {
1020                 download_in_progress = 1;
1021                 bytes = extract_long(cret, 0);
1022                 last_mod = extract_int(cret, 1);
1023                 extract(mimetype, cret, 2);
1024                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1025                 ret = CtdlIPCEndDownload(cret);
1026                 if (ret / 100 == 2)
1027                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1028                                         filename, mimetype);
1029         }
1030         return ret;
1031 }
1032
1033
1034 /* OPNA */
1035 int CtdlIPCAttachmentDownload(long msgnum, const char *part, void **buf,
1036                 char *cret)
1037 {
1038         register int ret;
1039         size_t bytes;
1040         time_t last_mod;
1041         char filename[SIZ];
1042         char mimetype[SIZ];
1043         char *aaa;
1044
1045         if (!cret) return -2;
1046         if (!buf) return -2;
1047         if (*buf) return -2;
1048         if (!part) return -2;
1049         if (!msgnum) return -2;
1050         if (download_in_progress) return -2;
1051
1052         aaa = (char *)malloc(strlen(part) + 17);
1053         if (!aaa) return -1;
1054
1055         sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1056         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1057         free(aaa);
1058         /* FIXME: Possible race condition */
1059         if (ret / 100 == 2) {
1060                 download_in_progress = 1;
1061                 bytes = extract_long(cret, 0);
1062                 last_mod = extract_int(cret, 1);
1063                 extract(mimetype, cret, 2);
1064                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1065                 ret = CtdlIPCEndDownload(cret);
1066                 if (ret / 100 == 2)
1067                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1068                                         filename, mimetype);
1069         }
1070         return ret;
1071 }
1072
1073
1074 /* OIMG */
1075 int CtdlIPCImageDownload(const char *filename, void **buf, char *cret)
1076 {
1077         register int ret;
1078         size_t bytes;
1079         time_t last_mod;
1080         char mimetype[SIZ];
1081         char *aaa;
1082
1083         if (!cret) return -1;
1084         if (!buf) return -1;
1085         if (*buf) return -1;
1086         if (!filename) return -1;
1087         if (download_in_progress) return -1;
1088
1089         aaa = (char *)malloc(strlen(filename) + 6);
1090         if (!aaa) return -1;
1091
1092         sprintf(aaa, "OIMG %s", filename);
1093         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1094         free(aaa);
1095         /* FIXME: Possible race condition */
1096         if (ret / 100 == 2) {
1097                 download_in_progress = 1;
1098                 bytes = extract_long(cret, 0);
1099                 last_mod = extract_int(cret, 1);
1100                 extract(mimetype, cret, 2);
1101                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1102                 ret = CtdlIPCEndDownload(cret);
1103                 if (ret / 100 == 2)
1104                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1105                                         filename, mimetype);
1106         }
1107         return ret;
1108 }
1109
1110
1111 /* UOPN */
1112 int CtdlIPCFileUpload(const char *filename, const char *comment, void *buf,
1113                 size_t bytes, char *cret)
1114 {
1115         register int ret;
1116         char *aaa;
1117
1118         if (!cret) return -1;
1119         if (!filename) return -1;
1120         if (!comment) return -1;
1121         if (upload_in_progress) return -1;
1122
1123         aaa = (char *)malloc(strlen(filename) + strlen(comment) + 7);
1124         if (!aaa) return -1;
1125
1126         sprintf(aaa, "UOPN %s|%s", filename, comment);
1127         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1128         free(aaa);
1129         /* FIXME: Possible race condition */
1130         if (ret / 100 == 2)
1131                 upload_in_progress = 1;
1132         ret = CtdlIPCWriteUpload(buf, bytes, cret);
1133         ret = CtdlIPCEndUpload(cret);
1134         return ret;
1135 }
1136
1137
1138 /* UIMG */
1139 int CtdlIPCImageUpload(int for_real, const char *filename, size_t bytes,
1140                 char *cret)
1141 {
1142         register int ret;
1143         char *aaa;
1144
1145         if (!cret) return -1;
1146         if (!filename) return -1;
1147         if (upload_in_progress) return -1;
1148
1149         aaa = (char *)malloc(strlen(filename) + 17);
1150         if (!aaa) return -1;
1151
1152         sprintf(aaa, "UIMG %d|%s", for_real, filename);
1153         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1154         free(aaa);
1155         /* FIXME: Possible race condition */
1156         if (ret / 100 == 2)
1157                 upload_in_progress = 1;
1158         return ret;
1159 }
1160
1161
1162 /* QUSR */
1163 int CtdlIPCQueryUsername(const char *username, char *cret)
1164 {
1165         register int ret;
1166         char *aaa;
1167
1168         if (!cret) return -2;
1169         if (!username) return -2;
1170
1171         aaa = (char *)malloc(strlen(username) + 6);
1172         if (!aaa) return -1;
1173
1174         sprintf(aaa, "QUSR %s", username);
1175         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1176         free(aaa);
1177         return ret;
1178 }
1179
1180
1181 /* LFLR */
1182 int CtdlIPCFloorListing(char **listing, char *cret)
1183 {
1184         size_t bytes;
1185
1186         if (!cret) return -2;
1187         if (!listing) return -2;
1188         if (*listing) return -2;
1189
1190         return CtdlIPCGenericCommand("LFLR", NULL, 0, listing, &bytes, cret);
1191 }
1192
1193
1194 /* CFLR */
1195 int CtdlIPCCreateFloor(int for_real, const char *name, char *cret)
1196 {
1197         register int ret;
1198         char *aaa;
1199
1200         if (!cret) return -2;
1201         if (!name) return -2;
1202
1203         aaa = (char *)malloc(strlen(name) + 17);
1204         if (!aaa) return -1;
1205
1206         sprintf(aaa, "CFLR %s|%d", name, for_real);
1207         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1208         free(aaa);
1209         return ret;
1210 }
1211
1212
1213 /* KFLR */
1214 int CtdlIPCDeleteFloor(int for_real, int floornum, char *cret)
1215 {
1216         char aaa[27];
1217
1218         if (!cret) return -1;
1219         if (floornum < 0) return -1;
1220
1221         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1222         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1223 }
1224
1225
1226 /* EFLR */
1227 int CtdlIPCEditFloor(int floornum, const char *floorname, char *cret)
1228 {
1229         register int ret;
1230         char *aaa;
1231
1232         if (!cret) return -2;
1233         if (!floorname) return -2;
1234         if (floornum < 0) return -2;
1235
1236         aaa = (char *)malloc(strlen(floorname) + 17);
1237         if (!aaa) return -1;
1238
1239         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1240         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1241         free(aaa);
1242         return ret;
1243 }
1244
1245
1246 /* IDEN */
1247 int CtdlIPCIdentifySoftware(int developerid, int clientid, int revision,
1248                 const char *software_name, const char *hostname, char *cret)
1249 {
1250         register int ret;
1251         char *aaa;
1252
1253         if (developerid < 0) return -2;
1254         if (clientid < 0) return -2;
1255         if (revision < 0) return -2;
1256         if (!software_name) return -2;
1257         if (!hostname) return -2;
1258
1259         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1260         if (!aaa) return -1;
1261
1262         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1263                         revision, software_name, hostname);
1264         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1265         free(aaa);
1266         return ret;
1267 }
1268
1269
1270 /* SEXP */
1271 int CtdlIPCSendInstantMessage(const char *username, const char *text,
1272                 char *cret)
1273 {
1274         register int ret;
1275         char *aaa;
1276
1277         if (!cret) return -2;
1278         if (!username) return -2;
1279
1280         aaa = (char *)malloc(strlen(username) + 8);
1281         if (!aaa) return -1;
1282
1283         if (text) {
1284                 sprintf(aaa, "SEXP %s|-", username);
1285                 ret = CtdlIPCGenericCommand(aaa, text, strlen(text),
1286                                 NULL, NULL, cret);
1287         } else {
1288                 sprintf(aaa, "SEXP %s||", username);
1289                 ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1290         }
1291         free(aaa);
1292         return ret;
1293 }
1294
1295
1296 /* GEXP */
1297 int CtdlIPCGetInstantMessage(char **listing, char *cret)
1298 {
1299         size_t bytes;
1300
1301         if (!cret) return -2;
1302         if (!listing) return -2;
1303         if (*listing) return -2;
1304
1305         return CtdlIPCGenericCommand("GEXP", NULL, 0, listing, &bytes, cret);
1306 }
1307
1308
1309 /* DEXP */
1310 /* mode is 0 = enable, 1 = disable, 2 = status */
1311 int CtdlIPCEnableInstantMessageReceipt(int mode, char *cret)
1312 {
1313         char aaa[16];
1314
1315         if (!cret) return -2;
1316
1317         sprintf(aaa, "DEXP %d", mode);
1318         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1319 }
1320
1321
1322 /* EBIO */
1323 int CtdlIPCSetBio(char *bio, char *cret)
1324 {
1325         if (!cret) return -2;
1326         if (!bio) return -2;
1327
1328         return CtdlIPCGenericCommand("EBIO", bio, strlen(bio),
1329                         NULL, NULL, cret);
1330 }
1331
1332
1333 /* RBIO */
1334 int CtdlIPCGetBio(const char *username, char **listing, char *cret)
1335 {
1336         register int ret;
1337         size_t bytes;
1338         char *aaa;
1339
1340         if (!cret) return -2;
1341         if (!username) return -2;
1342         if (!listing) return -2;
1343         if (*listing) return -2;
1344
1345         aaa = (char *)malloc(strlen(username) + 6);
1346         if (!aaa) return -1;
1347
1348         sprintf(aaa, "RBIO %s", username);
1349         ret = CtdlIPCGenericCommand(aaa, NULL, 0, listing, &bytes, cret);
1350         free(aaa);
1351         return ret;
1352 }
1353
1354
1355 /* LBIO */
1356 int CtdlIPCListUsersWithBios(char **listing, char *cret)
1357 {
1358         size_t bytes;
1359
1360         if (!cret) return -2;
1361         if (!listing) return -2;
1362         if (*listing) return -2;
1363
1364         return CtdlIPCGenericCommand("LBIO", NULL, 0, listing, &bytes, cret);
1365 }
1366
1367
1368 /* STEL */
1369 int CtdlIPCStealthMode(int mode, char *cret)
1370 {
1371         char aaa[16];
1372
1373         if (!cret) return -1;
1374
1375         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1376         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1377 }
1378
1379
1380 /* TERM */
1381 int CtdlIPCTerminateSession(int sid, char *cret)
1382 {
1383         char aaa[16];
1384
1385         if (!cret) return -1;
1386
1387         sprintf(aaa, "TERM %d", sid);
1388         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1389 }
1390
1391
1392 /* DOWN */
1393 int CtdlIPCTerminateServerNow(char *cret)
1394 {
1395         if (!cret) return -1;
1396
1397         return CtdlIPCGenericCommand("DOWN", NULL, 0, NULL, NULL, cret);
1398 }
1399
1400
1401 /* SCDN */
1402 int CtdlIPCTerminateServerScheduled(int mode, char *cret)
1403 {
1404         char aaa[16];
1405
1406         if (!cret) return -1;
1407
1408         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1409         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1410 }
1411
1412
1413 /* EMSG */
1414 int CtdlIPCEnterSystemMessage(const char *filename, const char *text,
1415                 char *cret)
1416 {
1417         register int ret;
1418         char *aaa;
1419
1420         if (!cret) return -2;
1421         if (!text) return -2;
1422         if (!filename) return -2;
1423
1424         aaa = (char *)malloc(strlen(filename) + 6);
1425         if (!aaa) return -1;
1426
1427         sprintf(aaa, "EMSG %s", filename);
1428         ret = CtdlIPCGenericCommand(aaa, text, strlen(text), NULL, NULL, cret);
1429         free(aaa);
1430         return ret;
1431 }
1432
1433
1434 /* HCHG */
1435 int CtdlIPCChangeHostname(const char *hostname, char *cret)
1436 {
1437         register int ret;
1438         char *aaa;
1439
1440         if (!cret) return -2;
1441         if (!hostname) return -2;
1442
1443         aaa = (char *)malloc(strlen(hostname) + 6);
1444         if (!aaa) return -1;
1445
1446         sprintf(aaa, "HCHG %s", hostname);
1447         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1448         free(aaa);
1449         return ret;
1450 }
1451
1452
1453 /* RCHG */
1454 int CtdlIPCChangeRoomname(const char *roomname, char *cret)
1455 {
1456         register int ret;
1457         char *aaa;
1458
1459         if (!cret) return -2;
1460         if (!roomname) return -2;
1461
1462         aaa = (char *)malloc(strlen(roomname) + 6);
1463         if (!aaa) return -1;
1464
1465         sprintf(aaa, "RCHG %s", roomname);
1466         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1467         free(aaa);
1468         return ret;
1469 }
1470
1471
1472 /* UCHG */
1473 int CtdlIPCChangeUsername(const char *username, char *cret)
1474 {
1475         register int ret;
1476         char *aaa;
1477
1478         if (!cret) return -2;
1479         if (!username) return -2;
1480
1481         aaa = (char *)malloc(strlen(username) + 6);
1482         if (!aaa) return -1;
1483
1484         sprintf(aaa, "UCHG %s", username);
1485         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1486         free(aaa);
1487         return ret;
1488 }
1489
1490
1491 /* TIME */
1492 /* This function returns the actual server time reported, or 0 if error */
1493 time_t CtdlIPCServerTime(char *cret)
1494 {
1495         register time_t tret;
1496         register int ret;
1497
1498         ret = CtdlIPCGenericCommand("TIME", NULL, 0, NULL, NULL, cret);
1499         if (ret / 100 == 2) {
1500                 tret = extract_long(cret, 0);
1501         } else {
1502                 tret = 0L;
1503         }
1504         return tret;
1505 }
1506
1507
1508 /* AGUP */
1509 int CtdlIPCAideGetUserParameters(const char *who,
1510                                  struct usersupp **uret, char *cret)
1511 {
1512         register int ret;
1513         char *aaa;
1514
1515         if (!cret) return -2;
1516         if (!uret) return -2;
1517         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof(struct usersupp));
1518         if (!*uret) return -1;
1519
1520         aaa = (char *)malloc(strlen(uret[0]->fullname) + 6);
1521         if (!aaa) return -1;
1522
1523         sprintf(aaa, "AGUP %s", who);
1524         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1525         if (ret / 100 == 2) {
1526                 extract(uret[0]->fullname, cret, 0);
1527                 extract(uret[0]->password, cret, 1);
1528                 uret[0]->flags = extract_int(cret, 2);
1529                 uret[0]->timescalled = extract_long(cret, 3);
1530                 uret[0]->posted = extract_long(cret, 4);
1531                 uret[0]->axlevel = extract_int(cret, 5);
1532                 uret[0]->usernum = extract_long(cret, 6);
1533                 uret[0]->lastcall = extract_long(cret, 7);
1534                 uret[0]->USuserpurge = extract_int(cret, 8);
1535         }
1536         free(aaa);
1537         return ret;
1538 }
1539
1540
1541 /* ASUP */
1542 int CtdlIPCAideSetUserParameters(const struct usersupp *uret, char *cret)
1543 {
1544         register int ret;
1545         char *aaa;
1546
1547         if (!cret) return -2;
1548         if (!uret) return -2;
1549
1550         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1551         if (!aaa) return -1;
1552
1553         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1554                         uret->fullname, uret->password, uret->flags,
1555                         uret->timescalled, uret->posted, uret->axlevel,
1556                         uret->usernum, uret->lastcall, uret->USuserpurge);
1557         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1558         free(aaa);
1559         return ret;
1560 }
1561
1562
1563 /* GPEX */
1564 /* which is 0 = room, 1 = floor, 2 = site */
1565 int CtdlIPCGetMessageExpirationPolicy(int which, char *cret)
1566 {
1567         static char *proto[] = {"room", "floor", "site"};
1568         char aaa[11];
1569
1570         if (!cret) return -2;
1571         if (which < 0 || which > 2) return -2;
1572         
1573         sprintf(aaa, "GPEX %s", proto[which]);
1574         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1575 }
1576
1577
1578 /* SPEX */
1579 /* which is 0 = room, 1 = floor, 2 = site */
1580 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1581 int CtdlIPCSetMessageExpirationPolicy(int which, int policy, int value,
1582                 char *cret)
1583 {
1584         char aaa[38];
1585
1586         if (!cret) return -2;
1587         if (which < 0 || which > 2) return -2;
1588         if (policy < 0 || policy > 3) return -2;
1589         if (policy >= 2 && value < 1) return -2;
1590
1591         sprintf(aaa, "SPEX %d|%d|%d", which, policy, value);
1592         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1593 }
1594
1595
1596 /* CONF GET */
1597 int CtdlGetSystemConfig(char **listing, char *cret)
1598 {
1599         size_t bytes;
1600
1601         if (!cret) return -2;
1602         if (!listing) return -2;
1603         if (*listing) return -2;
1604
1605         return CtdlIPCGenericCommand("CONF GET", NULL, 0,
1606                         listing, &bytes, cret);
1607 }
1608
1609
1610 /* CONF SET */
1611 int CtdlSetSystemConfig(const char *listing, char *cret)
1612 {
1613         if (!cret) return -2;
1614         if (!listing) return -2;
1615
1616         return CtdlIPCGenericCommand("CONF SET", listing, strlen(listing),
1617                         NULL, NULL, cret);
1618 }
1619
1620
1621 /* MMOD */
1622 int CtdlIPCModerateMessage(long msgnum, int level, char *cret)
1623 {
1624         char aaa[27];
1625
1626         if (!cret) return -2;
1627         if (!msgnum) return -2;
1628
1629         sprintf(aaa, "MMOD %ld|%d", msgnum, level);
1630         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1631 }
1632
1633
1634 /* REQT */
1635 int CtdlIPCRequestClientLogout(int session, char *cret)
1636 {
1637         char aaa[16];
1638
1639         if (!cret) return -2;
1640         if (session < 0) return -2;
1641
1642         sprintf(aaa, "REQT %d", session);
1643         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1644 }
1645
1646
1647 /* SEEN */
1648 int CtdlIPCSetMessageSeen(long msgnum, int seen, char *cret)
1649 {
1650         char aaa[27];
1651
1652         if (!cret) return -2;
1653         if (msgnum < 0) return -2;
1654
1655         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1656         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1657 }
1658
1659
1660 /* STLS */
1661 int CtdlIPCStartEncryption(char *cret)
1662 {
1663         return CtdlIPCGenericCommand("STLS", NULL, 0, NULL, NULL, cret);
1664 }
1665
1666
1667 /* QDIR */
1668 int CtdlIPCDirectoryLookup(const char *address, char *cret)
1669 {
1670         char *aaa;
1671
1672         if (!address) return -2;
1673         if (!cret) return -2;
1674
1675         aaa = (char *)malloc(strlen(address) + 6);
1676         if (!aaa) return -1;
1677
1678         sprintf(aaa, "QDIR %s", address);
1679         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1680 }
1681
1682
1683 /*
1684  * Not implemented:
1685  * 
1686  * CHAT
1687  * ETLS
1688  * EXPI
1689  * GTLS
1690  * IGAB
1691  * IPGM
1692  * MSG3
1693  * MSG4
1694  * NDOP
1695  * NETP
1696  * NUOP
1697  * SMTP
1698  */
1699
1700
1701 /* ************************************************************************** */
1702 /*             Stuff below this line is not for public consumption            */
1703 /* ************************************************************************** */
1704
1705
1706 inline void netio_lock(void)
1707 {
1708 #ifdef THREADED_CLIENT
1709         pthread_mutex_lock(&rwlock);
1710 #endif
1711 }
1712
1713
1714 inline void netio_unlock(void)
1715 {
1716 #ifdef THREADED_CLIENT
1717         pthread_mutex_unlock(&rwlock);
1718 #endif
1719 }
1720
1721
1722 /* Read a listing from the server up to 000.  Append to dest if it exists */
1723 char *CtdlIPCReadListing(char *dest)
1724 {
1725         long length = 0;
1726         char *ret;
1727         char aaa[SIZ];
1728
1729         ret = dest;
1730         if (ret) length = strlen(ret);
1731         while (serv_gets(aaa), strcmp(aaa, "000")) {
1732                 ret = (char *)realloc(ret, length + strlen(aaa) + 2);
1733                 if (ret) {
1734                         strcpy(&ret[length], aaa);
1735                         length += strlen(aaa);
1736                         strcpy(&ret[length++], "\n");
1737                 }
1738         }
1739         return ret;
1740 }
1741
1742
1743 /* Send a listing to the server; generate the ending 000. */
1744 int CtdlIPCSendListing(const char *listing)
1745 {
1746         char *text;
1747
1748         text = (char *)malloc(strlen(listing) + 6);
1749         if (text) {
1750                 strcpy(text, listing);
1751                 while (text[strlen(text) - 1] == '\n')
1752                         text[strlen(text) - 1] = '\0';
1753                 strcat(text, "\n000");
1754                 serv_puts(text);
1755                 free(text);
1756                 text = NULL;
1757         } else {
1758                 /* Malloc failed but we are committed to send */
1759                 /* This may result in extra blanks at the bottom */
1760                 serv_puts(text);
1761                 serv_puts("000");
1762         }
1763         return 0;
1764 }
1765
1766
1767 /* Partial read of file from server */
1768 size_t CtdlIPCPartialRead(void **buf, size_t offset, size_t bytes, char *cret)
1769 {
1770         register size_t len = 0;
1771         char aaa[SIZ];
1772
1773         if (!buf) return -1;
1774         if (!cret) return -1;
1775         if (bytes < 1) return -1;
1776         if (offset < 0) return -1;
1777
1778         netio_lock();
1779         sprintf(aaa, "READ %d|%d", offset, bytes);
1780         serv_puts(aaa);
1781         serv_gets(aaa);
1782         if (aaa[0] != '6')
1783                 strcpy(cret, &aaa[4]);
1784         else {
1785                 len = extract_long(&aaa[4], 0);
1786                 *buf = (void *)realloc(*buf, offset + len);
1787                 if (*buf) {
1788                         /* I know what I'm doing */
1789                         serv_read((char *)&buf[offset], len);
1790                 } else {
1791                         /* We have to read regardless */
1792                         serv_read(aaa, len);
1793                         len = -1;
1794                 }
1795         }
1796         netio_unlock();
1797         return len;
1798 }
1799
1800
1801 /* CLOS */
1802 int CtdlIPCEndDownload(char *cret)
1803 {
1804         register int ret;
1805
1806         if (!cret) return -2;
1807         if (!download_in_progress) return -2;
1808
1809         ret = CtdlIPCGenericCommand("CLOS", NULL, 0, NULL, NULL, cret);
1810         if (ret / 100 == 2)
1811                 download_in_progress = 0;
1812         return ret;
1813 }
1814
1815
1816 /* READ */
1817 int CtdlIPCReadDownload(void **buf, size_t bytes, char *cret)
1818 {
1819         register size_t len;
1820
1821         if (!cret) return -1;
1822         if (!buf) return -1;
1823         if (*buf) return -1;
1824         if (!download_in_progress) return -1;
1825
1826         len = 0;
1827         while (len < bytes) {
1828                 len = CtdlIPCPartialRead(buf, len, 4096, cret);
1829                 if (len == -1) {
1830                         free(*buf);
1831                         return 0;
1832                 }
1833         }
1834         return len;
1835 }
1836
1837
1838 /* UCLS */
1839 int CtdlIPCEndUpload(char *cret)
1840 {
1841         register int ret;
1842
1843         if (!cret) return -1;
1844         if (!upload_in_progress) return -1;
1845
1846         ret = CtdlIPCGenericCommand("UCLS", NULL, 0, NULL, NULL, cret);
1847         if (ret / 100 == 2)
1848                 upload_in_progress = 0;
1849         return ret;
1850 }
1851
1852
1853 /* WRIT */
1854 int CtdlIPCWriteUpload(void *buf, size_t bytes, char *cret)
1855 {
1856         register int ret = -1;
1857         register size_t offset;
1858         char aaa[SIZ];
1859
1860         if (!cret) return -1;
1861         if (!buf) return -1;
1862         if (bytes < 1) return -1;
1863
1864         offset = 0;
1865         while (offset < bytes) {
1866                 sprintf(aaa, "WRIT %d", bytes - offset);
1867                 serv_puts(aaa);
1868                 serv_gets(aaa);
1869                 strcpy(cret, &aaa[4]);
1870                 ret = atoi(aaa);
1871                 if (aaa[0] == '7') {
1872                         register size_t to_write;
1873
1874                         to_write = extract_long(&aaa[4], 0);
1875                         serv_write(buf + offset, to_write);
1876                         offset += to_write;
1877                 } else {
1878                         break;
1879                 }
1880         }
1881         return ret;
1882 }
1883
1884
1885 /*
1886  * Generic command method.  This method should handle any server command
1887  * except for CHAT.  It takes the following arguments:
1888  *
1889  * command              Preformatted command to send to server
1890  * to_send              A text or binary file to send to server
1891  *                      (only sent if server requests it)
1892  * bytes_to_send        The number of bytes in to_send (required if
1893  *                      sending binary, optional if sending listing)
1894  * to_receive           Pointer to a NULL pointer, if the server
1895  *                      sends text or binary we will allocate memory
1896  *                      for the file and stuff it here
1897  * bytes_to_receive     If a file is received, we will store its
1898  *                      byte count here
1899  * proto_response       The protocol response.  Caller must provide
1900  *                      this buffer and ensure that it is at least
1901  *                      128 bytes in length.
1902  *
1903  * This function returns a number equal to the protocol response number,
1904  * -1 if an internal error occurred, -2 if caller provided bad values,
1905  * or 0 - the protocol response number if bad values were found during
1906  * the protocol exchange.
1907  * It stores the protocol response string (minus the number) in 
1908  * protocol_response as described above.  Some commands send additional
1909  * data in this string.
1910  */
1911 int CtdlIPCGenericCommand(const char *command, const char *to_send,
1912                 size_t bytes_to_send, char **to_receive, 
1913                 size_t *bytes_to_receive, char *proto_response)
1914 {
1915         char buf[SIZ];
1916         register int ret;
1917
1918         if (!command) return -2;
1919         if (!proto_response) return -2;
1920
1921         netio_lock();
1922         serv_puts((char *)command);
1923         while (1) {
1924                 serv_gets(proto_response);
1925                 if (proto_response[3] == '*')
1926                         express_msgs = 1;
1927                 ret = atoi(proto_response);
1928                 memmove(proto_response, &proto_response[4],
1929                                 strlen(proto_response) - 3);
1930                 switch (ret / 100) {
1931                 default:                        /* Unknown, punt */
1932                 case 2:                         /* OK */
1933                 case 3:                         /* MORE_DATA */
1934                 case 5:                         /* ERROR */
1935                         /* Don't need to do anything */
1936                         break;
1937                 case 1:                         /* LISTING_FOLLOWS */
1938                         if (to_receive && !*to_receive && bytes_to_receive) {
1939                                 *to_receive = CtdlIPCReadListing(NULL);
1940                         } else { /* Drain */
1941                                 while (serv_gets(buf), strcmp(buf, "000")) ;
1942                                 ret = -ret;
1943                         }
1944                         break;
1945                 case 4:                         /* SEND_LISTING */
1946                         if (to_send) {
1947                                 CtdlIPCSendListing(to_send);
1948                         } else {
1949                                 /* No listing given, fake it */
1950                                 serv_puts("000");
1951                                 ret = -ret;
1952                         }
1953                         break;
1954                 case 6:                         /* BINARY_FOLLOWS */
1955                         if (to_receive && !*to_receive && bytes_to_receive) {
1956                                 *bytes_to_receive =
1957                                         extract_long(proto_response, 0);
1958                                 *to_receive = (char *)malloc(*bytes_to_receive);
1959                                 if (!*to_receive) {
1960                                         ret = -1;
1961                                 } else {
1962                                         serv_read(*to_receive,
1963                                                         *bytes_to_receive);
1964                                 }
1965                         } else {
1966                                 /* Drain */
1967                                 size_t drain;
1968
1969                                 drain = extract_long(proto_response, 0);
1970                                 while (drain > SIZ) {
1971                                         serv_read(buf, SIZ);
1972                                         drain -= SIZ;
1973                                 }
1974                                 serv_read(buf, drain);
1975                                 ret = -ret;
1976                         }
1977                         break;
1978                 case 7:                         /* SEND_BINARY */
1979                         if (to_send && bytes_to_send) {
1980                                 serv_write((char *)to_send, bytes_to_send);
1981                         } else if (bytes_to_send) {
1982                                 /* Fake it, send nulls */
1983                                 size_t fake;
1984
1985                                 fake = bytes_to_send;
1986                                 memset(buf, '\0', SIZ);
1987                                 while (fake > SIZ) {
1988                                         serv_write(buf, SIZ);
1989                                         fake -= SIZ;
1990                                 }
1991                                 serv_write(buf, fake);
1992                                 ret = -ret;
1993                         } /* else who knows?  DANGER WILL ROBINSON */
1994                         break;
1995                 case 8:                         /* START_CHAT_MODE */
1996                         if (!strncasecmp(command, "CHAT", 4)) {
1997                                 /* Don't call chatmode with generic! */
1998                                 serv_puts("/quit");
1999                                 ret = -ret;
2000                         } else {
2001                                 /* In this mode we send then receive listing */
2002                                 if (to_send) {
2003                                         CtdlIPCSendListing(to_send);
2004                                 } else {
2005                                         /* No listing given, fake it */
2006                                         serv_puts("000");
2007                                         ret = -ret;
2008                                 }
2009                                 if (to_receive && !*to_receive
2010                                                 && bytes_to_receive) {
2011                                         *to_receive = CtdlIPCReadListing(NULL);
2012                                 } else { /* Drain */
2013                                         while (serv_gets(buf),
2014                                                         strcmp(buf, "000")) ;
2015                                         ret = -ret;
2016                                 }
2017                         }
2018                         break;
2019                 case 9:                         /* ASYNC_MSG */
2020                         /* CtdlIPCDoAsync(ret, proto_response); */
2021                         free(CtdlIPCReadListing(NULL)); /* STUB FIXME */
2022                         break;
2023                 }
2024                 if (ret / 100 != 9)
2025                         break;
2026         }
2027         netio_unlock();
2028         return ret;
2029 }