1
2 /*
3 * Osmo - a handy personal organizer
4 *
5 * Copyright (C) 2015 Tomasz Mąka <pasp@users.sourceforge.net>
6 * 2015 Piotr Mąka <silloz@users.sourceforge.net>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23 #include <stdlib.h>
24
25 #include "vcf.h"
26 #include "gui.h"
27 #include <stdlib.h>
28 #include <glib.h>
29 #include <string.h>
30
31 #define CRLF "\r\n"
32 #define CRLF_SIZE 2
33 #define DELIM_COMPONENT ';'
34 #define DELIM_LIST ','
35
36 #define MAX_LINE_SIZE 75
37 #define MAX_FOLDED_LINE_SIZE 74
38 #define LINE_FOLD CRLF " "
39 #define LINE_FOLD_SIZE 3
40 #define MAX_ESCAPE_SIZE 2
41
42 #define BEGIN "BEGIN:VCARD"
43 #define END "END:VCARD"
44 #define VERSION_40 "VERSION:4.0"
45
46 #define FN "FN"
47 #define N "N"
48 #define NICKNAME "NICKNAME"
49 #define BDAY "BDAY"
50 #define ADR "ADR"
51 #define ORG "ORG"
52 #define TEL "TEL"
53 #define EMAIL "EMAIL"
54 #define URL "URL"
55 #define IMPP "IMPP"
56 #define NOTE "NOTE"
57
58 #define TYPE_PARAM "TYPE"
59 #define PREF_PARAM "PREF"
60
61 #define TEL_URI_SCHEME "tel"
62
63 struct vcf_writer {
64 vcf_writer_callback *callback;
65 void *user_data;
66 };
67
68 static void write_string(vcf_writer *writer, gchar const *content);
69 static void write_value(vcf_writer *writer, gchar const *property_name, gchar const *value);
70 static void write_multivalue(vcf_writer *writer, gchar const *property_name, gchar separator, gint component_count, ...);
71 static gchar *format_date(guint32 julian_day);
72
73 /*------------------------------------------------------------------------------*/
74
75 vcf_writer *
vcf_create_writer(vcf_writer_callback * callback,void * user_data)76 vcf_create_writer(vcf_writer_callback *callback, void *user_data) {
77 vcf_writer *writer = g_new(vcf_writer, 1);
78 if(!writer) {
79 return NULL;
80 }
81 writer->callback = callback;
82 writer->user_data = user_data;
83
84 return writer;
85 }
86
87 void
vcf_close_writer(vcf_writer * writer)88 vcf_close_writer(vcf_writer *writer) {
89 g_free(writer);
90 }
91
92
93 void
vcf_write_begin(vcf_writer * writer)94 vcf_write_begin(vcf_writer *writer) {
95 write_string(writer, BEGIN);
96 write_string(writer, VERSION_40);
97 }
98
99 void
vcf_write_end(vcf_writer * writer)100 vcf_write_end(vcf_writer *writer) {
101 write_string(writer, END);
102 }
103
104 void
vcf_write_FN(vcf_writer * writer,char const * first_name,char const * last_name)105 vcf_write_FN(vcf_writer *writer, char const *first_name, char const *last_name) {
106 gchar *buffer;
107
108 if(first_name && last_name) {
109 buffer = g_strdup_printf("%s %s", first_name, last_name);
110 } else if(first_name) {
111 buffer = g_strdup(first_name);
112 }else {
113 buffer = g_strdup(last_name);
114 }
115
116 write_value(writer, FN, buffer);
117
118 g_free(buffer);
119 }
120
121 void
vcf_write_N(vcf_writer * writer,char const * last_name,char const * first_name,char const * second_name)122 vcf_write_N(vcf_writer *writer, char const *last_name, char const *first_name, char const *second_name) {
123 write_multivalue(writer, N, DELIM_COMPONENT, 5, last_name, first_name, second_name, NULL, NULL);
124 }
125
126 void
vcf_write_NICKNAME(vcf_writer * writer,char const * nick_name)127 vcf_write_NICKNAME(vcf_writer *writer, char const *nick_name) {
128 write_multivalue(writer, NICKNAME, DELIM_LIST, 1, nick_name);
129 }
130
131 void
vcf_write_BDAY(vcf_writer * writer,guint32 julian_day)132 vcf_write_BDAY(vcf_writer *writer, guint32 julian_day) {
133 gchar *text = format_date(julian_day);
134 g_return_if_fail(text);
135
136 write_value(writer, BDAY, text);
137 g_free(text);
138 }
139
140 void
vcf_write_ADR(vcf_writer * writer,char const * type,char const * address,char const * city,char const * state,char const * post_code,char const * country)141 vcf_write_ADR(vcf_writer *writer, char const *type, char const *address, char const *city, char const *state, char const *post_code, char const *country) {
142 gchar *property = g_strdup_printf("%s;%s=%s", ADR, TYPE_PARAM, type);
143 write_multivalue(writer, property, DELIM_COMPONENT, 7, "", "", address, city, state, post_code, country);
144 g_free(property);
145 }
146
147 void
vcf_write_ORG(vcf_writer * writer,char const * organization,char const * department)148 vcf_write_ORG(vcf_writer *writer, char const *organization, char const *department) {
149 write_multivalue(writer, ORG, DELIM_COMPONENT, 2, organization, department);
150 }
151
152 void
vcf_write_TEL_internal(vcf_writer * writer,gchar const * number,gint pref,gchar const ** types)153 vcf_write_TEL_internal(vcf_writer *writer, gchar const *number, gint pref, gchar const **types) {
154 gchar *joined_types, *property, *number_uri;
155 g_return_if_fail(pref >= 1 && pref <=100);
156
157 joined_types = g_strjoinv (";" TYPE_PARAM "=", (gchar **) types);
158 property = g_strdup_printf("%s;%s=%d%s%s", TEL, PREF_PARAM, pref, ";" TYPE_PARAM "=", joined_types);
159 number_uri = g_strdup_printf("%s:%s", TEL_URI_SCHEME, number);
160
161 write_value(writer, property, number_uri);
162
163 g_free(number_uri);
164 g_free(property);
165 g_free(joined_types);
166 }
167
168 void
vcf_write_EMAIL(vcf_writer * writer,gchar const * email,gint pref)169 vcf_write_EMAIL(vcf_writer *writer, gchar const *email, gint pref) {
170 gchar *property;
171 g_return_if_fail(pref >= 1 && pref <=100);
172
173 property = g_strdup_printf("%s;%s=%d", EMAIL, PREF_PARAM, pref);
174
175 write_value(writer, property, email);
176
177 g_free(property);
178 }
179
180 void
vcf_write_URL(vcf_writer * writer,gchar const * url)181 vcf_write_URL(vcf_writer *writer, gchar const *url) {
182 write_value(writer, URL, url);
183 }
184
185 void
vcf_write_URL_pref(vcf_writer * writer,gchar const * url,gint pref)186 vcf_write_URL_pref(vcf_writer *writer, gchar const *url, gint pref) {
187 gchar *property;
188 g_return_if_fail(pref >= 1 && pref <= 100);
189
190 property = g_strdup_printf("%s;%s=%d", URL, PREF_PARAM, pref);
191
192 write_value(writer, property, url);
193
194 g_free(property);
195 }
196 void
vcf_write_IMPP(vcf_writer * writer,gchar const * url,gchar const * type)197 vcf_write_IMPP(vcf_writer *writer, gchar const *url, gchar const *type) {
198 gchar *property = g_strdup_printf("%s;%s=%s", IMPP, TYPE_PARAM, type);
199
200 write_value(writer, property, url);
201
202 g_free(property);
203 }
204
205 void
vcf_write_NOTE(vcf_writer * writer,gchar const * note)206 vcf_write_NOTE(vcf_writer *writer, gchar const *note) {
207 write_value(writer, NOTE, note);
208 }
209
210 /*------------------------------------------------------------------------------*/
211 static void
replace_char(gchar * string,gchar search,gchar const * replacement)212 replace_char(gchar *string, gchar search, gchar const *replacement) {
213 gchar *buffer, *tmp;
214 gsize buffer_size, replacement_size;
215 gint pos = 0;
216
217 replacement_size = strlen(replacement);
218 buffer_size = strlen(string);
219 if(!buffer_size) {
220 return;
221 }
222 buffer = g_new(gchar, buffer_size*replacement_size+1);
223
224 tmp = string;
225 while (*tmp) {
226 if (*tmp == search) {
227 strcat(buffer, replacement);
228 pos += replacement_size;
229 } else {
230 buffer[pos] = *tmp;
231 buffer[pos+1] = '\0';
232 pos++;
233 }
234 tmp++;
235 }
236 strcpy(string, buffer);
237 g_free(buffer);
238 }
239
240 static void
escape_value(gchar * string)241 escape_value(gchar *string) {
242 replace_char(string, '\\', "\\\\");
243 replace_char(string, DELIM_COMPONENT, "\\;");
244 replace_char(string, DELIM_LIST, "\\,");
245 replace_char(string, '\n', "\\n");
246 }
247
248 static void
write_multivalue(vcf_writer * writer,gchar const * property,gchar separator,gint component_count,...)249 write_multivalue(vcf_writer *writer, gchar const *property, gchar separator, gint component_count, ...) {
250 va_list arguments;
251 gchar **components;
252 gint i, buffer_size;
253 gchar *buffer;
254
255 components = g_new(gchar *, component_count);
256
257 buffer_size = strlen(property) + 1; /* property */
258 va_start(arguments, component_count);
259 for (i = 0; i < component_count; i++) {
260 components[i] = va_arg(arguments, gchar *);
261 if (components[i]) {
262 buffer_size += (strlen(components[i]) * MAX_ESCAPE_SIZE); /* escaped value */
263 }
264 }
265 va_end(arguments);
266 buffer_size += (component_count - 1); /* separators */
267 buffer_size++;
268
269 buffer = g_new(gchar, buffer_size);
270
271 g_snprintf(buffer, buffer_size, "%s:", property);
272 for (i = 0; i < component_count; i++) {
273 if (i) {
274 gint len = strlen(buffer);
275 buffer[len++] = separator;
276 buffer[len] = '\0';
277 }
278 if (components[i]) {
279 gchar *component_buffer;
280 gint len = strlen(components[i]);
281 component_buffer = g_new(gchar, (len * MAX_ESCAPE_SIZE + 1));
282 strcpy(component_buffer, components[i]);
283 escape_value(component_buffer);
284 strcat(buffer, component_buffer);
285 g_free(component_buffer);
286 }
287 }
288
289 write_string(writer, buffer);
290
291 g_free(buffer);
292 g_free(components);
293 }
294
295 static void
write_value(vcf_writer * writer,gchar const * property_name,gchar const * value)296 write_value(vcf_writer *writer, gchar const *property_name, gchar const * value) {
297 write_multivalue(writer, property_name, '\0', 1, value);
298 }
299
300 static gchar *
format_date(guint32 julian_day)301 format_date(guint32 julian_day) {
302 gchar *buffer;
303 GDate *cdate;
304 gsize size = 9;
305
306 buffer = g_new(gchar, size);
307 cdate = g_date_new_julian (julian_day);
308 size = g_date_strftime (buffer, size, "%Y%m%d", cdate);
309 if(!size) {
310 g_free(buffer);
311 return NULL;
312 }
313 return buffer;
314 }
315
316 static void
write_string(vcf_writer * writer,gchar const * content)317 write_string(vcf_writer *writer, gchar const *content) {
318 gchar *buffer;
319 gsize max_len;
320
321 buffer = g_new(gchar, MAX_LINE_SIZE + 1);
322 g_return_if_fail(buffer);
323
324 max_len = MAX_LINE_SIZE;
325 while (*content) {
326 gsize len = strlen(content);
327 if (len > max_len) {
328 gchar *end = g_utf8_find_prev_char(content, content + MAX_LINE_SIZE);
329 len = (end - content);
330 strncpy(buffer, content, len);
331 buffer[len] = '\0';
332 writer->callback(buffer, len, writer->user_data);
333 writer->callback(LINE_FOLD, LINE_FOLD_SIZE, writer->user_data);
334
335 content = end;
336 max_len = MAX_FOLDED_LINE_SIZE;
337 } else {
338 strcpy(buffer, content);
339 writer->callback(buffer, len, writer->user_data);
340 writer->callback(CRLF, CRLF_SIZE, writer->user_data);
341 content += len;
342 }
343 }
344 g_free(buffer);
345 }