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