citadel
About: Citadel is an advanced messaging and collaboration system for groupware and BBS applications (preferred OS: Linux).
  Fossies Dox: citadel.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

serv_image.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 1987-2021 by the citadel.org team
3 *
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include "ctdl_module.h"
16#include "config.h"
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <dirent.h>
20
21
22/*
23 * DownLoad Room Image (see its icon or whatever)
24 * If this command succeeds, it follows the same protocol as the DLAT command.
25 */
26void cmd_dlri(char *cmdbuf) {
28 if (CC->room.msgnum_pic < 1) {
29 cprintf("%d No image found.\n", ERROR + FILE_NOT_FOUND);
30 return;
31 }
32
33 struct CtdlMessage *msg = CtdlFetchMessage(CC->room.msgnum_pic, 1);
34 if (msg != NULL) {
35 // The call to CtdlOutputPreLoadedMsg() with MT_SPEW_SECTION will cause the DLRI command
36 // to have the same output format as the DLAT command, because it calls the same code.
37 // For example: 600 402132|-1||image/gif|
38 safestrncpy(CC->download_desired_section, "1", sizeof CC->download_desired_section);
40 CM_Free(msg);
41 }
42 else {
43 cprintf("%d No image found.\n", ERROR + MESSAGE_NOT_FOUND);
44 return;
45 }
46}
47
48
49/*
50 * UpLoad Room Image (avatar or photo or whatever)
51 */
52void cmd_ulri(char *cmdbuf) {
53 long data_length;
54 char mimetype[SIZ];
55
56 if (CtdlAccessCheck(ac_room_aide)) return;
57
58 data_length = extract_long(cmdbuf, 0);
59 extract_token(mimetype, cmdbuf, 1, '|', sizeof mimetype);
60
61 if (data_length < 20) {
62 cprintf("%d That's an awfully small file. Try again.\n", ERROR + ILLEGAL_VALUE);
63 return;
64 }
65
66 if (strncasecmp(mimetype, "image/", 6)) {
67 cprintf("%d Only image files are permitted.\n", ERROR + ILLEGAL_VALUE);
68 return;
69 }
70
71 char *unencoded_data = malloc(data_length + 1);
72 if (!unencoded_data) {
73 cprintf("%d Could not allocate %ld bytes of memory\n", ERROR + INTERNAL_ERROR , data_length);
74 return;
75 }
76
77 cprintf("%d %ld\n", SEND_BINARY, data_length);
78 client_read(unencoded_data, data_length);
79
80 // We've got the data read from the client, now save it.
81 char *encoded_data = malloc((data_length * 2) + 100);
82 if (encoded_data) {
83 sprintf(encoded_data, "Content-type: %s\nContent-transfer-encoding: base64\n\n", mimetype);
84 CtdlEncodeBase64(&encoded_data[strlen(encoded_data)], unencoded_data, data_length, 1);
85 long new_msgnum = quickie_message("Citadel", NULL, NULL, SYSCONFIGROOM, encoded_data, FMT_RFC822, "Image uploaded by admin user");
86
87 if (CtdlGetRoomLock(&CC->room, CC->room.QRname) == 0) {
88 long old_msgnum = CC->room.msgnum_pic;
89 syslog(LOG_DEBUG, "Message %ld is now the photo for %s", new_msgnum, CC->room.QRname);
90 CC->room.msgnum_pic = new_msgnum;
91 CtdlPutRoomLock(&CC->room);
92 if (old_msgnum > 0) {
93 syslog(LOG_DEBUG, "Deleting old message %ld from %s", old_msgnum, SYSCONFIGROOM);
94 CtdlDeleteMessages(SYSCONFIGROOM, &old_msgnum, 1, "");
95 }
96 }
97 free(encoded_data);
98 }
99
100 free(unencoded_data);
101}
102
103
104/*
105 * DownLoad User Image (see their avatar or photo or whatever)
106 * If this command succeeds, it follows the same protocol as the DLAT command.
107 */
108void cmd_dlui(char *cmdbuf) {
109 struct ctdluser ruser;
110 char buf[SIZ];
111
113 extract_token(buf, cmdbuf, 0, '|', sizeof buf);
114 if (CtdlGetUser(&ruser, buf) != 0) {
115 cprintf("%d No such user.\n", ERROR + NO_SUCH_USER);
116 return;
117 }
118 if (ruser.msgnum_pic < 1) {
119 cprintf("%d No image found.\n", ERROR + FILE_NOT_FOUND);
120 return;
121 }
122
123 struct CtdlMessage *msg = CtdlFetchMessage(ruser.msgnum_pic, 1);
124 if (msg != NULL) {
125 // The call to CtdlOutputPreLoadedMsg() with MT_SPEW_SECTION will cause the DLUI command
126 // to have the same output format as the DLAT command, because it calls the same code.
127 // For example: 600 402132|-1||image/gif|
128 safestrncpy(CC->download_desired_section, "1", sizeof CC->download_desired_section);
130 CM_Free(msg);
131 }
132 else {
133 cprintf("%d No image found.\n", ERROR + MESSAGE_NOT_FOUND);
134 return;
135 }
136}
137
138
139/*
140 * UpLoad User Image (avatar or photo or whatever)
141 */
142void cmd_ului(char *cmdbuf) {
143 long data_length;
144 char mimetype[SIZ];
145 char username[USERNAME_SIZE];
146 char userconfigroomname[ROOMNAMELEN];
147
149
150 if (num_parms(cmdbuf) < 2) {
151 cprintf("%d Usage error\n", ERROR + ILLEGAL_VALUE);
152 return;
153 }
154
155 data_length = extract_long(cmdbuf, 0);
156 extract_token(mimetype, cmdbuf, 1, '|', sizeof mimetype);
157 extract_token(username, cmdbuf, 2, '|', sizeof username);
158
159 if (data_length < 20) {
160 cprintf("%d That's an awfully small file. Try again.\n", ERROR + ILLEGAL_VALUE);
161 return;
162 }
163
164 if (strncasecmp(mimetype, "image/", 6)) {
165 cprintf("%d Only image files are permitted.\n", ERROR + ILLEGAL_VALUE);
166 return;
167 }
168
169 if (IsEmptyStr(username)) {
170 safestrncpy(username, CC->curr_user, sizeof username);
171 }
172
173 // Normal users can only change their own photo
174 if ( (strcasecmp(username, CC->curr_user)) && (CC->user.axlevel < AxAideU) && (!CC->internal_pgm) ) {
175 cprintf("%d Higher access required to change another user's photo.\n", ERROR + HIGHER_ACCESS_REQUIRED);
176 }
177
178 // Check to make sure the user exists
179 struct ctdluser usbuf;
180 if (CtdlGetUser(&usbuf, username) != 0) { // check for existing user, don't lock it yet
181 cprintf("%d %s not found.\n", ERROR + NO_SUCH_USER , username);
182 return;
183 }
184 CtdlMailboxName(userconfigroomname, sizeof userconfigroomname, &usbuf, USERCONFIGROOM);
185
186 char *unencoded_data = malloc(data_length + 1);
187 if (!unencoded_data) {
188 cprintf("%d Could not allocate %ld bytes of memory\n", ERROR + INTERNAL_ERROR , data_length);
189 return;
190 }
191
192 cprintf("%d %ld\n", SEND_BINARY, data_length);
193 client_read(unencoded_data, data_length);
194
195 // We've got the data read from the client, now save it.
196 char *encoded_data = malloc((data_length * 2) + 100);
197 if (encoded_data) {
198 sprintf(encoded_data, "Content-type: %s\nContent-transfer-encoding: base64\n\n", mimetype);
199 CtdlEncodeBase64(&encoded_data[strlen(encoded_data)], unencoded_data, data_length, 1);
200 long new_msgnum = quickie_message("Citadel", NULL, NULL, userconfigroomname, encoded_data, FMT_RFC822, "Photo uploaded by user");
201
202 if (CtdlGetUserLock(&usbuf, username) == 0) { // lock it this time
203 long old_msgnum = usbuf.msgnum_pic;
204 syslog(LOG_DEBUG, "Message %ld is now the photo for %s", new_msgnum, username);
205 usbuf.msgnum_pic = new_msgnum;
207 if (old_msgnum > 0) {
208 syslog(LOG_DEBUG, "Deleting old message %ld from %s", old_msgnum, userconfigroomname);
209 CtdlDeleteMessages(userconfigroomname, &old_msgnum, 1, "");
210 }
211 }
212
213 free(encoded_data);
214 }
215
216 free(unencoded_data);
217}
218
219
220/*
221 * Import function called by import_old_userpic_files() for a single user
222 */
223void import_one_userpic_file(char *username, long usernum, char *path) {
224 syslog(LOG_DEBUG, "Import legacy userpic for %s, usernum=%ld, filename=%s", username, usernum, path);
225
226 FILE *fp = fopen(path, "r");
227 if (!fp) return;
228
229 fseek(fp, 0, SEEK_END);
230 long data_length = ftell(fp);
231
232 if (data_length >= 1) {
233 rewind(fp);
234 char *unencoded_data = malloc(data_length);
235 if (unencoded_data) {
236 fread(unencoded_data, data_length, 1, fp);
237 char *encoded_data = malloc((data_length * 2) + 100);
238 if (encoded_data) {
239 sprintf(encoded_data, "Content-type: %s\nContent-transfer-encoding: base64\n\n", GuessMimeByFilename(path, strlen(path)));
240 CtdlEncodeBase64(&encoded_data[strlen(encoded_data)], unencoded_data, data_length, 1);
241
242 char userconfigroomname[ROOMNAMELEN];
243 struct ctdluser usbuf;
244
245 if (CtdlGetUser(&usbuf, username) == 0) { // no need to lock it , we are still initializing
246 long old_msgnum = usbuf.msgnum_pic;
247 CtdlMailboxName(userconfigroomname, sizeof userconfigroomname, &usbuf, USERCONFIGROOM);
248 long new_msgnum = quickie_message("Citadel", NULL, NULL, userconfigroomname, encoded_data, FMT_RFC822, "Photo imported from file");
249 syslog(LOG_DEBUG, "Message %ld is now the photo for %s", new_msgnum, username);
250 usbuf.msgnum_pic = new_msgnum;
252 unlink(path); // delete the old file , it's in the database now
253 if (old_msgnum > 0) {
254 syslog(LOG_DEBUG, "Deleting old message %ld from %s", old_msgnum, userconfigroomname);
255 CtdlDeleteMessages(userconfigroomname, &old_msgnum, 1, "");
256 }
257 }
258 free(encoded_data);
259 }
260 free(unencoded_data);
261 }
262 }
263 fclose(fp);
264}
265
266
267/*
268 * Look for old-format "userpic" files and import them into the message base
269 */
271 DIR *filedir = NULL;
272 struct dirent *filedir_entry;
273 size_t d_namelen;
274 struct ctdluser usbuf;
275 long usernum = 0;
276 int d_type = 0;
277 struct stat s;
278 char path[PATH_MAX];
279
280
281 syslog(LOG_DEBUG, "Importing old style userpic files into the message base");
282 filedir = opendir (ctdl_usrpic_dir);
283 if (filedir == NULL) {
284 return;
285 }
286 while ( (filedir_entry = readdir(filedir)) , (filedir_entry != NULL))
287 {
288#ifdef _DIRENT_HAVE_D_NAMLEN
289 d_namelen = filedir_entry->d_namlen;
290
291#else
292 d_namelen = strlen(filedir_entry->d_name);
293#endif
294
295#ifdef _DIRENT_HAVE_D_TYPE
296 d_type = filedir_entry->d_type;
297#else
298
299#ifndef DT_UNKNOWN
300#define DT_UNKNOWN 0
301#define DT_DIR 4
302#define DT_REG 8
303#define DT_LNK 10
304
305#define IFTODT(mode) (((mode) & 0170000) >> 12)
306#define DTTOIF(dirtype) ((dirtype) << 12)
307#endif
308 d_type = DT_UNKNOWN;
309#endif
310 if ((d_namelen == 1) &&
311 (filedir_entry->d_name[0] == '.'))
312 continue;
313
314 if ((d_namelen == 2) &&
315 (filedir_entry->d_name[0] == '.') &&
316 (filedir_entry->d_name[1] == '.'))
317 continue;
318
319 snprintf(path, PATH_MAX, "%s/%s", ctdl_usrpic_dir, filedir_entry->d_name);
320 if (d_type == DT_UNKNOWN) {
321 if (lstat(path, &s) == 0) {
322 d_type = IFTODT(s.st_mode);
323 }
324 }
325 switch (d_type)
326 {
327 case DT_DIR:
328 break;
329 case DT_LNK:
330 case DT_REG:
331 usernum = atol(filedir_entry->d_name);
332 if (CtdlGetUserByNumber(&usbuf, usernum) == 0) {
333 import_one_userpic_file(usbuf.fullname, usernum, path);
334 }
335 }
336 }
337 closedir(filedir);
338 rmdir(ctdl_usrpic_dir);
339}
340
341
342
344{
345 if (!threading)
346 {
348 CtdlRegisterProtoHook(cmd_dlri, "DLRI", "DownLoad Room Image");
349 CtdlRegisterProtoHook(cmd_ulri, "ULRI", "UpLoad Room Image");
350 CtdlRegisterProtoHook(cmd_dlui, "DLUI", "DownLoad User Image");
351 CtdlRegisterProtoHook(cmd_ului, "ULUI", "UpLoad User Image");
352 }
353 /* return our module name for the log */
354 return "image";
355}
#define ROOMNAMELEN
Definition: citadel.h:56
#define USERNAME_SIZE
Definition: citadel.h:57
#define ctdl_usrpic_dir
Definition: citadel_dirs.h:15
#define CC
Definition: context.h:140
int CtdlAccessCheck(int)
Definition: user_ops.c:389
void CtdlPutUser(struct ctdluser *usbuf)
Definition: user_ops.c:128
void CtdlMailboxName(char *buf, size_t n, const struct ctdluser *who, const char *prefix)
Definition: user_ops.c:351
int CtdlGetUserByNumber(struct ctdluser *usbuf, long number)
Definition: user_ops.c:462
int CtdlGetUserLock(struct ctdluser *usbuf, char *name)
Definition: user_ops.c:113
int CtdlGetRoomLock(struct ctdlroom *qrbuf, const char *room_name)
Definition: room_ops.c:387
void CtdlPutUserLock(struct ctdluser *usbuf)
Definition: user_ops.c:147
@ ac_room_aide
Definition: ctdl_module.h:245
@ ac_logged_in_or_guest
Definition: ctdl_module.h:243
int CtdlGetUser(struct ctdluser *usbuf, char *name)
Definition: user_ops.c:77
void CtdlPutRoomLock(struct ctdlroom *qrbuf)
Definition: room_ops.c:444
void CtdlRegisterProtoHook(void(*handler)(char *), char *cmd, char *desc)
#define CTDL_MODULE_INIT(module_name)
Definition: ctdl_module.h:50
#define ILLEGAL_VALUE
Definition: ipcdef.h:16
#define INTERNAL_ERROR
Definition: ipcdef.h:14
#define HIGHER_ACCESS_REQUIRED
Definition: ipcdef.h:23
#define SEND_BINARY
Definition: ipcdef.h:11
#define FILE_NOT_FOUND
Definition: ipcdef.h:30
#define ERROR
Definition: ipcdef.h:9
#define MESSAGE_NOT_FOUND
Definition: ipcdef.h:34
#define NO_SUCH_USER
Definition: ipcdef.h:29
void CM_Free(struct CtdlMessage *msg)
Definition: msgbase.c:310
int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, int mode, int headers_only, int do_proto, int crlf, int flags)
Definition: msgbase.c:1969
struct CtdlMessage * CtdlFetchMessage(long msgnum, int with_body)
Definition: msgbase.c:1135
long quickie_message(const char *from, const char *fromaddr, const char *to, char *room, const char *text, int format_type, const char *subject)
Definition: msgbase.c:2921
int CtdlDeleteMessages(const char *room_name, long *dmsgnums, int num_dmsgnums, char *content_type)
Definition: msgbase.c:3240
#define HEADERS_NONE
Definition: msgbase.h:41
void * malloc(size_t)
void free(void *)
void cmd_ului(char *cmdbuf)
Definition: serv_image.c:142
void cmd_dlui(char *cmdbuf)
Definition: serv_image.c:108
#define DT_DIR
#define DT_UNKNOWN
void cmd_ulri(char *cmdbuf)
Definition: serv_image.c:52
void import_one_userpic_file(char *username, long usernum, char *path)
Definition: serv_image.c:223
#define DT_REG
#define DT_LNK
void cmd_dlri(char *cmdbuf)
Definition: serv_image.c:26
void import_old_userpic_files(void)
Definition: serv_image.c:270
#define IFTODT(mode)
struct ctdluser usbuf
Definition: serv_migrate.c:496
#define FMT_RFC822
Definition: server.h:178
@ MT_SPEW_SECTION
Definition: server.h:170
int snprintf(char *buf, size_t max, const char *fmt,...)
Definition: snprintf.c:69
char fullname[64]
Definition: citadel.h:90
long usernum
Definition: citadel.h:87
long msgnum_pic
Definition: citadel.h:92
#define USERCONFIGROOM
Definition: sysconfig.h:64
#define SIZ
Definition: sysconfig.h:33
#define SYSCONFIGROOM
Definition: sysconfig.h:72
void cprintf(const char *format,...)
Definition: sysdep.c:381
int client_read(char *buf, int bytes)
Definition: sysdep.c:514