1 /*
2  * Claws Mail templates subsystem
3  * Copyright (C) 2001 Alexander Barinov
4  * Copyright (C) 2001-2018 The Claws Mail team
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include "defs.h"
22 
23 #include <glib.h>
24 #include <glib/gi18n.h>
25 #include <stdio.h>
26 #include <sys/stat.h>
27 #include <ctype.h>
28 
29 #include "utils.h"
30 #include "template.h"
31 #include "codeconv.h"
32 #include "file-utils.h"
33 
34 static GSList *template_list;
35 
template_load(gchar * filename)36 static Template *template_load(gchar *filename)
37 {
38 	Template *tmpl;
39 	FILE *fp;
40 	gchar buf[BUFFSIZE];
41 	gint bytes_read;
42 
43 	if ((fp = claws_fopen(filename, "rb")) == NULL) {
44 		FILE_OP_ERROR(filename, "claws_fopen");
45 		return NULL;
46 	}
47 
48 	tmpl = g_new(Template, 1);
49 	tmpl->load_filename = g_strdup(filename);;
50 	tmpl->name = NULL;
51 	tmpl->subject = NULL;
52 	tmpl->from = NULL;
53 	tmpl->to = NULL;
54 	tmpl->cc = NULL;
55 	tmpl->bcc = NULL;
56 	tmpl->replyto = NULL;
57 	tmpl->value = NULL;
58 
59 	while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
60 		if (buf[0] == '\n')
61 			break;
62 		else if (!g_ascii_strncasecmp(buf, "Name:", 5))
63 			tmpl->name = g_strdup(g_strstrip(buf + 5));
64 		else if (!g_ascii_strncasecmp(buf, "From:", 5))
65 			tmpl->from = g_strdup(g_strstrip(buf + 5));
66 		else if (!g_ascii_strncasecmp(buf, "To:", 3))
67 			tmpl->to = g_strdup(g_strstrip(buf + 3));
68 		else if (!g_ascii_strncasecmp(buf, "Cc:", 3))
69 			tmpl->cc = g_strdup(g_strstrip(buf + 3));
70 		else if (!g_ascii_strncasecmp(buf, "Bcc:", 4))
71 			tmpl->bcc = g_strdup(g_strstrip(buf + 4));
72 		else if (!g_ascii_strncasecmp(buf, "Reply-To:", 9))
73 			tmpl->replyto = g_strdup(g_strstrip(buf + 9));
74 		else if (!g_ascii_strncasecmp(buf, "Subject:", 8))
75 			tmpl->subject = g_strdup(g_strstrip(buf + 8));
76 	}
77 
78 	if (!tmpl->name) {
79 		g_warning("wrong template format");
80 		template_free(tmpl);
81 		claws_fclose(fp);
82 		return NULL;
83 	}
84 
85 	if ((bytes_read = claws_fread(buf, 1, sizeof(buf), fp)) == 0) {
86 		if (claws_ferror(fp)) {
87 			FILE_OP_ERROR(filename, "claws_fread");
88 			template_free(tmpl);
89 			claws_fclose(fp);
90 			return NULL;
91 		}
92 	}
93 	claws_fclose(fp);
94 	tmpl->value = g_strndup(buf, bytes_read);
95 
96 	return tmpl;
97 }
98 
template_free(Template * tmpl)99 void template_free(Template *tmpl)
100 {
101 	g_free(tmpl->load_filename);
102 	g_free(tmpl->name);
103 	g_free(tmpl->subject);
104 	g_free(tmpl->from);
105 	g_free(tmpl->to);
106 	g_free(tmpl->cc);
107 	g_free(tmpl->bcc);
108 	g_free(tmpl->replyto);
109 	g_free(tmpl->value);
110 	g_free(tmpl);
111 }
112 
template_clear_config(GSList * tmpl_list)113 static void template_clear_config(GSList *tmpl_list)
114 {
115 	GSList *cur;
116 	Template *tmpl;
117 
118 	for (cur = tmpl_list; cur != NULL; cur = cur->next) {
119 		tmpl = (Template *)cur->data;
120 		template_free(tmpl);
121 	}
122 	g_slist_free(tmpl_list);
123 }
124 
tmpl_compare(gconstpointer tmpl1,gconstpointer tmpl2)125 static gint tmpl_compare(gconstpointer tmpl1, gconstpointer tmpl2)
126 {
127 	gchar *basename1, *basename2;
128 	long filenum1, filenum2;
129 	gint ret = 0;
130 
131 	if ((Template *)tmpl1 == NULL || (Template *)tmpl2 == NULL)
132 		return 0;
133 
134 	if (((Template *)tmpl1)->load_filename == NULL || ((Template *)tmpl2)->load_filename == NULL)
135 		return 0;
136 
137 	basename1 = g_path_get_basename(((Template *)tmpl1)->load_filename);
138 	basename2 = g_path_get_basename(((Template *)tmpl2)->load_filename);
139 	filenum1 = atol(basename1);
140 	filenum2 = atol(basename2);
141 	g_free(basename1);
142 	g_free(basename2);
143 
144 	if (filenum1 == 0 || filenum2 == 0)
145 		return 0;
146 
147 	if (filenum1 < filenum2)
148 		ret = -1;
149 	else
150 		if (filenum1 > filenum2)
151 			ret = 1;
152 
153 	return ret;
154 }
155 
template_read_config(void)156 GSList *template_read_config(void)
157 {
158 	const gchar *path;
159 	gchar *filename;
160 	GDir *dir;
161 	const gchar *dir_name;
162 	GStatBuf s;
163 	Template *tmpl;
164 	GSList *tmpl_list = NULL;
165 
166 	path = get_template_dir();
167 	debug_print("%s:%d reading templates dir %s\n",
168 		    __FILE__, __LINE__, path);
169 
170 	if (!is_dir_exist(path)) {
171 		if (make_dir(path) < 0)
172 			return NULL;
173 	}
174 
175 	if ((dir = g_dir_open(path, 0, NULL)) == NULL) {
176 		g_warning("failed to open directory: '%s'", path);
177 		return NULL;
178 	}
179 
180 	while ((dir_name = g_dir_read_name(dir)) != NULL) {
181 		filename = g_strconcat(path, G_DIR_SEPARATOR_S,
182 				       dir_name, NULL);
183 
184 		if (g_stat(filename, &s) != 0 || !S_ISREG(s.st_mode) ) {
185 			debug_print("%s:%d %s is not an ordinary file\n",
186 				    __FILE__, __LINE__, filename);
187 			continue;
188 		}
189 
190 		tmpl = template_load(filename);
191 		if (tmpl)
192 			tmpl_list = g_slist_insert_sorted(tmpl_list, tmpl, tmpl_compare);
193 
194 		g_free(filename);
195 	}
196 
197 	g_dir_close(dir);
198 
199 	return tmpl_list;
200 }
201 
202 #define TRY(func) { \
203 if (!(func)) \
204 { \
205 	g_warning("Failed to write template to file"); \
206 	if (fp) claws_fclose(fp); \
207 	if (new) claws_unlink(new); \
208 	g_free(new); \
209 	g_free(filename); \
210 	return; \
211 } \
212 }
213 
214 #define TRY_NO_CLOSE(func) { \
215 if (!(func)) \
216 { \
217 	g_warning("Failed to write template to file"); \
218 	if (new) claws_unlink(new); \
219 	g_free(new); \
220 	g_free(filename); \
221 	return; \
222 } \
223 }
224 
template_write_config(GSList * tmpl_list)225 static void template_write_config(GSList *tmpl_list)
226 {
227 	const gchar *path;
228 	GSList *cur;
229 	Template *tmpl;
230 	FILE *fp;
231 	gint tmpl_num;
232 
233 	debug_print("%s:%d writing templates\n", __FILE__, __LINE__);
234 
235 	path = get_template_dir();
236 
237 	if (!is_dir_exist(path)) {
238 		if (is_file_exist(path)) {
239 			g_warning("file '%s' already exists", path);
240 			return;
241 		}
242 		if (make_dir(path) < 0)
243 			return;
244 	}
245 
246 	for (cur = tmpl_list, tmpl_num = 1; cur != NULL;
247 	     cur = cur->next, tmpl_num++) {
248 		gchar *filename, *new = NULL;
249 
250 		tmpl = cur->data;
251 
252 		filename = g_strconcat(path, G_DIR_SEPARATOR_S,
253 				       itos(tmpl_num), NULL);
254 
255 		if (is_file_exist(filename)) {
256 			new = g_strconcat(filename, ".new", NULL);
257 		}
258 
259 		if ((fp = claws_fopen(new?new:filename, "wb")) == NULL) {
260 			FILE_OP_ERROR(new?new:filename, "claws_fopen");
261 			g_free(new);
262 			g_free(filename);
263 			return;
264 		}
265 
266 		TRY(fprintf(fp, "Name: %s\n", tmpl->name) > 0);
267 		if (tmpl->subject && *tmpl->subject != '\0')
268 			TRY(fprintf(fp, "Subject: %s\n", tmpl->subject) > 0);
269 		if (tmpl->from && *tmpl->from != '\0')
270 			TRY(fprintf(fp, "From: %s\n", tmpl->from) > 0);
271 		if (tmpl->to && *tmpl->to != '\0')
272 			TRY(fprintf(fp, "To: %s\n", tmpl->to) > 0);
273 		if (tmpl->cc && *tmpl->cc != '\0')
274 			TRY(fprintf(fp, "Cc: %s\n", tmpl->cc) > 0);
275 		if (tmpl->bcc && *tmpl->bcc != '\0')
276 			TRY(fprintf(fp, "Bcc: %s\n", tmpl->bcc) > 0);
277 		if (tmpl->replyto && *tmpl->replyto != '\0')
278 			TRY(fprintf(fp, "Reply-To: %s\n", tmpl->replyto) > 0);
279 
280 		TRY(claws_fputs("\n", fp) != EOF);
281 
282 		if (tmpl->value && *tmpl->value != '\0') {
283 			TRY(claws_fwrite(tmpl->value, sizeof(gchar), strlen(tmpl->value), fp) == strlen(tmpl->value));
284 		} else {
285 			TRY(claws_fwrite("", sizeof(gchar), 1, fp) == 1);
286 		}
287 		TRY_NO_CLOSE(claws_safe_fclose(fp) != EOF);
288 
289 		if (new) {
290 			if (rename_force(new, filename) < 0) {
291 				FILE_OP_ERROR(new, "rename");
292 			}
293 		}
294 		g_free(new);
295 		g_free(filename);
296 	}
297 
298 	/* remove other templates */
299 	while (TRUE) {
300 		gchar *filename = g_strconcat(path, G_DIR_SEPARATOR_S,
301 				       itos(tmpl_num), NULL);
302 		if (is_file_exist(filename)) {
303 			debug_print("removing old template %d\n", tmpl_num);
304 			claws_unlink(filename);
305 			g_free(filename);
306 		} else {
307 			g_free(filename);
308 			break;
309 		}
310 		tmpl_num++;
311 	}
312 }
313 
template_get_config(void)314 GSList *template_get_config(void)
315 {
316 	if (!template_list)
317 		template_list = template_read_config();
318 
319 	return template_list;
320 }
321 
template_set_config(GSList * tmpl_list)322 void template_set_config(GSList *tmpl_list)
323 {
324 	template_clear_config(template_list);
325 	template_write_config(tmpl_list);
326 	template_list = tmpl_list;
327 }
328