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