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