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