Contents of /trunk/src/osm_api.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 240 - (show annotations)
Thu Jul 23 18:50:17 2009 UTC (14 years, 9 months ago) by harbaum
File MIME type: text/plain
File size: 34082 byte(s)
Usage of base object completed
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of OSM2Go.
5 *
6 * OSM2Go 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 * OSM2Go 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 OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "appdata.h"
21
22 #include <curl/curl.h>
23 #include <curl/types.h> /* new for v7 */
24 #include <curl/easy.h> /* new for v7 */
25 #include <unistd.h>
26
27 #define COLOR_ERR "red"
28 #define COLOR_OK "darkgreen"
29
30 #define NO_EXPECT
31
32 static struct http_message_s {
33 int id;
34 char *msg;
35 } http_messages [] = {
36 { 200, "Ok" },
37 { 203, "No Content" },
38 { 301, "Moved Permenently" },
39 { 302, "Moved Temporarily" },
40 { 400, "Bad Request" },
41 { 401, "Unauthorized" },
42 { 403, "Forbidden" },
43 { 404, "Not Found" },
44 { 405, "Method Not Allowed" },
45 { 409, "Conflict" },
46 { 410, "Gone" },
47 { 412, "Precondition Failed" },
48 { 417, "(Expect rejected)" },
49 { 500, "Internal Server Error" },
50 { 503, "Service Unavailable" },
51 { 0, NULL }
52 };
53
54 static char *osm_http_message(int id) {
55 struct http_message_s *msg = http_messages;
56
57 while(msg->id) {
58 if(msg->id == id) return _(msg->msg);
59 msg++;
60 }
61
62 return NULL;
63 }
64
65 typedef struct {
66 appdata_t *appdata;
67 GtkWidget *dialog;
68 osm_t *osm;
69 project_t *project;
70
71 struct log_s {
72 GtkTextBuffer *buffer;
73 GtkWidget *view;
74 } log;
75
76 item_id_t changeset;
77 char *comment;
78
79 proxy_t *proxy;
80 } osm_upload_context_t;
81
82 gboolean osm_download(GtkWidget *parent, settings_t *settings,
83 project_t *project) {
84 printf("download osm ...\n");
85
86 g_assert(project->server);
87
88 /* check if server name contains string "0.5" and adjust it */
89 if(strstr(project->server, "0.5") != NULL) {
90 strstr(project->server, "0.5")[2] = '6';
91
92 messagef(parent, _("Server changed"),
93 _("It seems your current project uses a server/protocol no "
94 "longer in use by OSM. It has thus been changed to:\n\n%s"),
95 project->server);
96 } else
97 printf("url ok\n");
98
99 char minlon[G_ASCII_DTOSTR_BUF_SIZE], minlat[G_ASCII_DTOSTR_BUF_SIZE];
100 char maxlon[G_ASCII_DTOSTR_BUF_SIZE], maxlat[G_ASCII_DTOSTR_BUF_SIZE];
101
102 g_ascii_formatd(minlon, sizeof(minlon), LL_FORMAT, project->min.lon);
103 g_ascii_formatd(minlat, sizeof(minlat), LL_FORMAT, project->min.lat);
104 g_ascii_formatd(maxlon, sizeof(maxlon), LL_FORMAT, project->max.lon);
105 g_ascii_formatd(maxlat, sizeof(maxlat), LL_FORMAT, project->max.lat);
106
107 /* server url should not end with a slash */
108 if(project->server[strlen(project->server)-1] == '/') {
109 printf("removing trailing slash\n");
110 project->server[strlen(project->server)-1] = 0;
111 }
112
113 char *url = g_strdup_printf("%s/map?bbox=%s,%s,%s,%s",
114 project->server, minlon, minlat, maxlon, maxlat);
115
116 char *str = NULL;
117 if(project->osm[0] == '/') str = g_strdup(project->osm);
118 else str = g_strjoin(NULL, project->path, project->osm, NULL);
119 gboolean result = net_io_download_file(parent, settings, url, str);
120 g_free(str);
121
122 g_free(url);
123 return result;
124 }
125
126 typedef struct {
127 char *ptr;
128 int len;
129 } curl_data_t;
130
131 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
132 curl_data_t *p = (curl_data_t*)stream;
133
134 // printf("request to read %d items of size %d, pointer = %p\n",
135 // nmemb, size, p->ptr);
136
137 if(nmemb*size > p->len)
138 nmemb = p->len/size;
139
140 memcpy(ptr, p->ptr, size*nmemb);
141 p->ptr += size*nmemb;
142 p->len -= size*nmemb;
143
144 return nmemb;
145 }
146
147 static size_t write_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
148 curl_data_t *p = (curl_data_t*)stream;
149
150 p->ptr = g_realloc(p->ptr, p->len + size*nmemb + 1);
151 if(p->ptr) {
152 memcpy(p->ptr+p->len, ptr, size*nmemb);
153 p->len += size*nmemb;
154 p->ptr[p->len] = 0;
155 }
156 return nmemb;
157 }
158
159 static void appendf(struct log_s *log, char *colname,
160 const char *fmt, ...) {
161 va_list args;
162 va_start( args, fmt );
163 char *buf = g_strdup_vprintf(fmt, args);
164 va_end( args );
165
166 printf(buf);
167
168 GtkTextTag *tag = NULL;
169 if(colname)
170 tag = gtk_text_buffer_create_tag(log->buffer, NULL,
171 "foreground", colname,
172 NULL);
173
174 GtkTextIter end;
175 gtk_text_buffer_get_end_iter(log->buffer, &end);
176 if(tag)
177 gtk_text_buffer_insert_with_tags(log->buffer, &end, buf, -1, tag, NULL);
178 else
179 gtk_text_buffer_insert(log->buffer, &end, buf, -1);
180
181 g_free(buf);
182
183 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(log->view),
184 &end, 0.0, FALSE, 0, 0);
185
186 while(gtk_events_pending())
187 gtk_main_iteration();
188 }
189
190 #define MAX_TRY 5
191
192 static gboolean osm_update_item(struct log_s *log, char *xml_str,
193 char *url, char *user, item_id_t *id,
194 proxy_t *proxy) {
195 int retry = MAX_TRY;
196 char buffer[CURL_ERROR_SIZE];
197
198 CURL *curl;
199 CURLcode res;
200
201 curl_data_t read_data;
202 curl_data_t write_data;
203
204 while(retry >= 0) {
205
206 if(retry != MAX_TRY)
207 appendf(log, NULL, _("Retry %d/%d "), MAX_TRY-retry, MAX_TRY-1);
208
209 /* get a curl handle */
210 curl = curl_easy_init();
211 if(!curl) {
212 appendf(log, NULL, _("CURL init error\n"));
213 return FALSE;
214 }
215
216 read_data.ptr = xml_str;
217 read_data.len = xml_str?strlen(xml_str):0;
218 write_data.ptr = NULL;
219 write_data.len = 0;
220
221 /* we want to use our own read/write functions */
222 curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
223 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
224
225 /* enable uploading */
226 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
227
228 /* specify target URL, and note that this URL should include a file
229 name, not only a directory */
230 curl_easy_setopt(curl, CURLOPT_URL, url);
231
232 /* now specify which file to upload */
233 curl_easy_setopt(curl, CURLOPT_READDATA, &read_data);
234
235 /* provide the size of the upload, we specicially typecast the value
236 to curl_off_t since we must be sure to use the correct data size */
237 curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)read_data.len);
238
239 /* we pass our 'chunk' struct to the callback function */
240 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_data);
241
242 /* some servers don't like requests that are made without a user-agent
243 field, so we provide one */
244 curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE "-libcurl/" VERSION);
245
246 #ifdef NO_EXPECT
247 struct curl_slist *slist = NULL;
248 slist = curl_slist_append(slist, "Expect:");
249 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
250 #endif
251
252 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer);
253
254 /* set user name and password for the authentication */
255 curl_easy_setopt(curl, CURLOPT_USERPWD, user);
256
257 net_io_set_proxy(curl, proxy);
258
259 /* Now run off and do what you've been told! */
260 res = curl_easy_perform(curl);
261
262 long response;
263 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
264
265 /* always cleanup */
266 #ifdef NO_EXPECT
267 curl_slist_free_all(slist);
268 #endif
269 curl_easy_cleanup(curl);
270
271 /* this will return the id on a successful create */
272 if(id && (res == 0) && (response == 200)) {
273 printf("request to parse successful reply as an id\n");
274 *id = strtoul(write_data.ptr, NULL, 10);
275 }
276
277 if(res != 0)
278 appendf(log, COLOR_ERR, _("failed: %s\n"), buffer);
279 else if(response != 200)
280 appendf(log, COLOR_ERR, _("failed, code: %ld %s\n"),
281 response, osm_http_message(response));
282 else {
283 if(!id) appendf(log, COLOR_OK, _("ok\n"));
284 else appendf(log, COLOR_OK, _("ok: #%ld\n"), *id);
285 }
286
287 /* if it's neither "ok" (200), nor "internal server error" (500) */
288 /* then write the message to the log */
289 if((response != 200) && (response != 500) && write_data.ptr) {
290 appendf(log, NULL, _("Server reply: "));
291 appendf(log, COLOR_ERR, _("%s\n"), write_data.ptr);
292 }
293
294 if(write_data.ptr)
295 g_free(write_data.ptr);
296
297 /* don't retry unless we had an "internal server error" */
298 if(response != 500)
299 return((res == 0)&&(response == 200));
300
301 retry--;
302 }
303
304 return FALSE;
305 }
306
307 static gboolean osm_delete_item(struct log_s *log, char *xml_str,
308 char *url, char *user, proxy_t *proxy) {
309 int retry = MAX_TRY;
310 char buffer[CURL_ERROR_SIZE];
311
312 CURL *curl;
313 CURLcode res;
314
315 /* delete has a payload since api 0.6 */
316 curl_data_t read_data;
317 curl_data_t write_data;
318
319 while(retry >= 0) {
320
321 if(retry != MAX_TRY)
322 appendf(log, NULL, _("Retry %d/%d "), MAX_TRY-retry, MAX_TRY-1);
323
324 /* get a curl handle */
325 curl = curl_easy_init();
326 if(!curl) {
327 appendf(log, NULL, _("CURL init error\n"));
328 return FALSE;
329 }
330
331 read_data.ptr = xml_str;
332 read_data.len = xml_str?strlen(xml_str):0;
333 write_data.ptr = NULL;
334 write_data.len = 0;
335
336 /* we want to use our own read/write functions */
337 curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
338 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
339
340 curl_easy_setopt(curl, CURLOPT_INFILESIZE, (curl_off_t)read_data.len);
341
342 /* now specify which file to upload */
343 curl_easy_setopt(curl, CURLOPT_READDATA, &read_data);
344
345 /* we pass our 'chunk' struct to the callback function */
346 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_data);
347
348 /* enable uploading */
349 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
350
351 /* no read/write functions required */
352 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
353
354 /* specify target URL, and note that this URL should include a file
355 name, not only a directory */
356 curl_easy_setopt(curl, CURLOPT_URL, url);
357
358 /* some servers don't like requests that are made without a user-agent
359 field, so we provide one */
360 curl_easy_setopt(curl, CURLOPT_USERAGENT, PACKAGE "-libcurl/" VERSION);
361
362 #ifdef NO_EXPECT
363 struct curl_slist *slist = NULL;
364 slist = curl_slist_append(slist, "Expect:");
365 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
366 #endif
367
368 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer);
369
370 /* set user name and password for the authentication */
371 curl_easy_setopt(curl, CURLOPT_USERPWD, user);
372
373 net_io_set_proxy(curl, proxy);
374
375 /* Now run off and do what you've been told! */
376 res = curl_easy_perform(curl);
377
378 long response;
379 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
380
381 /* always cleanup */
382 #ifdef NO_EXPECT
383 curl_slist_free_all(slist);
384 #endif
385 curl_easy_cleanup(curl);
386
387 if(res != 0)
388 appendf(log, COLOR_ERR, _("failed: %s\n"), buffer);
389 else if(response != 200)
390 appendf(log, COLOR_ERR, _("failed, code: %ld %s\n"),
391 response, osm_http_message(response));
392 else
393 appendf(log, COLOR_OK, _("ok\n"));
394
395 /* if it's neither "ok" (200), nor "internal server error" (500) */
396 /* then write the message to the log */
397 if((response != 200) && (response != 500) && write_data.ptr) {
398 appendf(log, NULL, _("Server reply: "));
399 appendf(log, COLOR_ERR, _("%s\n"), write_data.ptr);
400 }
401
402 if(write_data.ptr)
403 g_free(write_data.ptr);
404
405 /* don't retry unless we had an "internal server error" */
406 if(response != 500)
407 return((res == 0)&&(response == 200));
408
409 retry--;
410 }
411
412 return FALSE;
413 }
414
415 typedef struct {
416 struct {
417 int total, new, dirty, deleted;
418 } ways, nodes, relations;
419 } osm_dirty_t;
420
421 static GtkWidget *table_attach_label_c(GtkWidget *table, char *str,
422 int x1, int x2, int y1, int y2) {
423 GtkWidget *label = gtk_label_new(str);
424 gtk_table_attach_defaults(GTK_TABLE(table), label, x1, x2, y1, y2);
425 return label;
426 }
427
428 static GtkWidget *table_attach_label_l(GtkWidget *table, char *str,
429 int x1, int x2, int y1, int y2) {
430 GtkWidget *label = table_attach_label_c(table, str, x1, x2, y1, y2);
431 gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
432 return label;
433 }
434
435 static GtkWidget *table_attach_int(GtkWidget *table, int num,
436 int x1, int x2, int y1, int y2) {
437 char *str = g_strdup_printf("%d", num);
438 GtkWidget *label = table_attach_label_c(table, str, x1, x2, y1, y2);
439 g_free(str);
440 return label;
441 }
442
443 static void osm_delete_nodes(osm_upload_context_t *context) {
444 node_t *node = context->osm->node;
445 project_t *project = context->project;
446
447 while(node) {
448 /* make sure gui gets updated */
449 while(gtk_events_pending()) gtk_main_iteration();
450
451 if(OSM_FLAGS(node) & OSM_FLAG_DELETED) {
452 printf("deleting node on server\n");
453
454 appendf(&context->log, NULL, _("Delete node #%ld "), OSM_ID(node));
455
456 char *url = g_strdup_printf("%s/node/" ITEM_ID_FORMAT,
457 project->server, OSM_ID(node));
458 char *cred = g_strdup_printf("%s:%s",
459 context->appdata->settings->username,
460 context->appdata->settings->password);
461
462 char *xml_str =
463 osm_generate_xml_node(context->osm, context->changeset, node);
464
465 if(osm_delete_item(&context->log, xml_str, url, cred, context->proxy)) {
466 OSM_FLAGS(node) &= ~(OSM_FLAG_DIRTY | OSM_FLAG_DELETED);
467 project->data_dirty = TRUE;
468 }
469
470 g_free(cred);
471 }
472 node = node->next;
473 }
474 }
475
476 static void osm_upload_nodes(osm_upload_context_t *context) {
477 node_t *node = context->osm->node;
478 project_t *project = context->project;
479
480 while(node) {
481 /* make sure gui gets updated */
482 while(gtk_events_pending()) gtk_main_iteration();
483
484 if((OSM_FLAGS(node) & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) &&
485 (!(OSM_FLAGS(node) & OSM_FLAG_DELETED))) {
486 char *url = NULL;
487
488 if(OSM_FLAGS(node) & OSM_FLAG_NEW) {
489 url = g_strdup_printf("%s/node/create", project->server);
490 appendf(&context->log, NULL, _("New node "));
491 } else {
492 url = g_strdup_printf("%s/node/" ITEM_ID_FORMAT,
493 project->server, OSM_ID(node));
494 appendf(&context->log, NULL, _("Modified node #%ld "), OSM_ID(node));
495 }
496
497 /* upload this node */
498 char *xml_str =
499 osm_generate_xml_node(context->osm, context->changeset, node);
500 if(xml_str) {
501 printf("uploading node %s from address %p\n", url, xml_str);
502
503 char *cred = g_strdup_printf("%s:%s",
504 context->appdata->settings->username,
505 context->appdata->settings->password);
506 if(osm_update_item(&context->log, xml_str, url, cred,
507 (OSM_FLAGS(node) & OSM_FLAG_NEW)?&(OSM_ID(node)):&OSM_VERSION(node),
508 context->proxy)) {
509 OSM_FLAGS(node) &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW);
510 project->data_dirty = TRUE;
511 }
512 g_free(cred);
513 }
514 g_free(url);
515 }
516 node = node->next;
517 }
518 }
519
520 static void osm_delete_ways(osm_upload_context_t *context) {
521 way_t *way = context->osm->way;
522 project_t *project = context->project;
523
524 while(way) {
525 /* make sure gui gets updated */
526 while(gtk_events_pending()) gtk_main_iteration();
527
528 if(OSM_FLAGS(way) & OSM_FLAG_DELETED) {
529 printf("deleting way on server\n");
530
531 appendf(&context->log, NULL, _("Delete way #%ld "), OSM_ID(way));
532
533 char *url = g_strdup_printf("%s/way/" ITEM_ID_FORMAT,
534 project->server, OSM_ID(way));
535 char *cred = g_strdup_printf("%s:%s",
536 context->appdata->settings->username,
537 context->appdata->settings->password);
538
539 char *xml_str =
540 osm_generate_xml_way(context->osm, context->changeset, way);
541
542 if(osm_delete_item(&context->log, xml_str, url, cred, context->proxy)) {
543 OSM_FLAGS(way) &= ~(OSM_FLAG_DIRTY | OSM_FLAG_DELETED);
544 project->data_dirty = TRUE;
545 }
546
547 g_free(cred);
548 }
549 way = way->next;
550 }
551 }
552
553
554 static void osm_upload_ways(osm_upload_context_t *context) {
555 way_t *way = context->osm->way;
556 project_t *project = context->project;
557
558 while(way) {
559 /* make sure gui gets updated */
560 while(gtk_events_pending()) gtk_main_iteration();
561
562 if((OSM_FLAGS(way) & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) &&
563 (!(OSM_FLAGS(way) & OSM_FLAG_DELETED))) {
564 char *url = NULL;
565
566 if(OSM_FLAGS(way) & OSM_FLAG_NEW) {
567 url = g_strdup_printf("%s/way/create", project->server);
568 appendf(&context->log, NULL, _("New way "));
569 } else {
570 url = g_strdup_printf("%s/way/" ITEM_ID_FORMAT,
571 project->server, OSM_ID(way));
572 appendf(&context->log, NULL, _("Modified way #%ld "), OSM_ID(way));
573 }
574
575 /* upload this node */
576 char *xml_str =
577 osm_generate_xml_way(context->osm, context->changeset, way);
578 if(xml_str) {
579 printf("uploading way %s from address %p\n", url, xml_str);
580
581 char *cred = g_strdup_printf("%s:%s",
582 context->appdata->settings->username,
583 context->appdata->settings->password);
584 if(osm_update_item(&context->log, xml_str, url, cred,
585 (OSM_FLAGS(way) & OSM_FLAG_NEW)?&(OSM_ID(way)):&OSM_VERSION(way),
586 context->proxy)) {
587 OSM_FLAGS(way) &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW);
588 project->data_dirty = TRUE;
589 }
590 g_free(cred);
591 }
592 g_free(url);
593 }
594 way = way->next;
595 }
596 }
597
598 static void osm_delete_relations(osm_upload_context_t *context) {
599 relation_t *relation = context->osm->relation;
600 project_t *project = context->project;
601
602 while(relation) {
603 /* make sure gui gets updated */
604 while(gtk_events_pending()) gtk_main_iteration();
605
606 if(OSM_FLAGS(relation) & OSM_FLAG_DELETED) {
607 printf("deleting relation on server\n");
608
609 appendf(&context->log, NULL,
610 _("Delete relation #%ld "), OSM_ID(relation));
611
612 char *url = g_strdup_printf("%s/relation/" ITEM_ID_FORMAT,
613 project->server, OSM_ID(relation));
614 char *cred = g_strdup_printf("%s:%s",
615 context->appdata->settings->username,
616 context->appdata->settings->password);
617
618 char *xml_str =
619 osm_generate_xml_relation(context->osm, context->changeset, relation);
620
621 if(osm_delete_item(&context->log, xml_str, url, cred, context->proxy)) {
622 OSM_FLAGS(relation) &= ~(OSM_FLAG_DIRTY | OSM_FLAG_DELETED);
623 project->data_dirty = TRUE;
624 }
625
626 g_free(cred);
627 }
628 relation = relation->next;
629 }
630 }
631
632
633 static void osm_upload_relations(osm_upload_context_t *context) {
634 relation_t *relation = context->osm->relation;
635 project_t *project = context->project;
636
637 while(relation) {
638 /* make sure gui gets updated */
639 while(gtk_events_pending()) gtk_main_iteration();
640
641 if((OSM_FLAGS(relation) & (OSM_FLAG_DIRTY | OSM_FLAG_NEW)) &&
642 (!(OSM_FLAGS(relation) & OSM_FLAG_DELETED))) {
643 char *url = NULL;
644
645 if(OSM_FLAGS(relation) & OSM_FLAG_NEW) {
646 url = g_strdup_printf("%s/relation/create", project->server);
647 appendf(&context->log, NULL, _("New relation "));
648 } else {
649 url = g_strdup_printf("%s/relation/" ITEM_ID_FORMAT,
650 project->server,OSM_ID(relation));
651 appendf(&context->log, NULL, _("Modified relation #%ld "),
652 OSM_ID(relation));
653 }
654
655 /* upload this relation */
656 char *xml_str =
657 osm_generate_xml_relation(context->osm, context->changeset, relation);
658 if(xml_str) {
659 printf("uploading relation %s from address %p\n", url, xml_str);
660
661 char *cred = g_strdup_printf("%s:%s",
662 context->appdata->settings->username,
663 context->appdata->settings->password);
664 if(osm_update_item(&context->log, xml_str, url, cred,
665 (OSM_FLAGS(relation) & OSM_FLAG_NEW)?&(OSM_ID(relation)):&
666 OSM_VERSION(relation), context->proxy)) {
667 OSM_FLAGS(relation) &= ~(OSM_FLAG_DIRTY | OSM_FLAG_NEW);
668 project->data_dirty = TRUE;
669 }
670 g_free(cred);
671 }
672 g_free(url);
673 }
674 relation = relation->next;
675 }
676 }
677
678 static gboolean osm_create_changeset(osm_upload_context_t *context) {
679 gboolean result = FALSE;
680 context->changeset = ILLEGAL;
681 project_t *project = context->project;
682
683 /* make sure gui gets updated */
684 while(gtk_events_pending()) gtk_main_iteration();
685
686 char *url = g_strdup_printf("%s/changeset/create", project->server);
687 appendf(&context->log, NULL, _("Create changeset "));
688
689 /* create changeset request */
690 char *xml_str = osm_generate_xml_changeset(context->osm, context->comment);
691 if(xml_str) {
692 printf("creating changeset %s from address %p\n", url, xml_str);
693
694 char *cred = g_strdup_printf("%s:%s",
695 context->appdata->settings->username,
696 context->appdata->settings->password);
697
698 if(osm_update_item(&context->log, xml_str, url, cred,
699 &context->changeset, context->proxy)) {
700 printf("got changeset id " ITEM_ID_FORMAT "\n", context->changeset);
701 result = TRUE;
702 }
703
704 g_free(cred);
705 }
706 g_free(url);
707
708 return result;
709 }
710
711 static gboolean osm_close_changeset(osm_upload_context_t *context) {
712 gboolean result = FALSE;
713 project_t *project = context->project;
714
715 g_assert(context->changeset != ILLEGAL);
716
717 /* make sure gui gets updated */
718 while(gtk_events_pending()) gtk_main_iteration();
719
720 char *url = g_strdup_printf("%s/changeset/" ITEM_ID_FORMAT "/close",
721 project->server, context->changeset);
722 appendf(&context->log, NULL, _("Close changeset "));
723
724 char *cred = g_strdup_printf("%s:%s",
725 context->appdata->settings->username,
726 context->appdata->settings->password);
727
728 if(osm_update_item(&context->log, NULL, url, cred, NULL, context->proxy))
729 result = TRUE;
730
731 g_free(cred);
732 g_free(url);
733
734 return result;
735 }
736
737 /* comment buffer has been edited, allow upload if the buffer is not empty */
738 static void callback_buffer_modified(GtkTextBuffer *buffer, GtkDialog *dialog) {
739 GtkTextIter start, end;
740 gtk_text_buffer_get_start_iter(buffer, &start);
741 gtk_text_buffer_get_end_iter(buffer, &end);
742 char *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
743 gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_ACCEPT,
744 text && strlen(text));
745 }
746
747 static gboolean cb_focus_in(GtkTextView *view, GdkEventFocus *event,
748 GtkTextBuffer *buffer) {
749
750 gboolean first_click =
751 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(view), "first_click"));
752
753 g_object_set_data(G_OBJECT(view), "first_click", GINT_TO_POINTER(FALSE));
754
755 if(first_click) {
756 GtkTextIter start, end;
757 gtk_text_buffer_get_start_iter(buffer, &start);
758 gtk_text_buffer_get_end_iter(buffer, &end);
759 gtk_text_buffer_delete(buffer, &start, &end);
760 }
761
762 return FALSE;
763 }
764
765 void osm_upload(appdata_t *appdata, osm_t *osm, project_t *project) {
766
767 printf("starting upload\n");
768
769 /* upload config and confirmation dialog */
770
771 /* count nodes */
772 osm_dirty_t dirty;
773 memset(&dirty, 0, sizeof(osm_dirty_t));
774
775 node_t *node = osm->node;
776 while(node) {
777 dirty.nodes.total++;
778 if(OSM_FLAGS(node) & OSM_FLAG_DELETED) dirty.nodes.deleted++;
779 else if(OSM_FLAGS(node) & OSM_FLAG_NEW) dirty.nodes.new++;
780 else if(OSM_FLAGS(node) & OSM_FLAG_DIRTY) dirty.nodes.dirty++;
781
782 node = node->next;
783 }
784 printf("nodes: new %2d, dirty %2d, deleted %2d\n",
785 dirty.nodes.new, dirty.nodes.dirty, dirty.nodes.deleted);
786
787 /* count ways */
788 way_t *way = osm->way;
789 while(way) {
790 dirty.ways.total++;
791 if(OSM_FLAGS(way) & OSM_FLAG_DELETED) dirty.ways.deleted++;
792 else if(OSM_FLAGS(way) & OSM_FLAG_NEW) dirty.ways.new++;
793 else if(OSM_FLAGS(way) & OSM_FLAG_DIRTY) dirty.ways.dirty++;
794
795 way = way->next;
796 }
797 printf("ways: new %2d, dirty %2d, deleted %2d\n",
798 dirty.ways.new, dirty.ways.dirty, dirty.ways.deleted);
799
800 /* count relations */
801 relation_t *relation = osm->relation;
802 while(relation) {
803 dirty.relations.total++;
804 if(OSM_FLAGS(relation) & OSM_FLAG_DELETED) dirty.relations.deleted++;
805 else if(OSM_FLAGS(relation) & OSM_FLAG_NEW) dirty.relations.new++;
806 else if(OSM_FLAGS(relation) & OSM_FLAG_DIRTY) dirty.relations.dirty++;
807
808 relation = relation->next;
809 }
810 printf("relations: new %2d, dirty %2d, deleted %2d\n",
811 dirty.relations.new, dirty.relations.dirty, dirty.relations.deleted);
812
813
814 GtkWidget *dialog =
815 misc_dialog_new(MISC_DIALOG_NOSIZE, _("Upload to OSM"),
816 GTK_WINDOW(appdata->window),
817 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
818 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
819 NULL);
820
821 GtkWidget *table = gtk_table_new(4, 5, TRUE);
822
823 table_attach_label_c(table, _("Total"), 1, 2, 0, 1);
824 table_attach_label_c(table, _("New"), 2, 3, 0, 1);
825 table_attach_label_c(table, _("Modified"), 3, 4, 0, 1);
826 table_attach_label_c(table, _("Deleted"), 4, 5, 0, 1);
827
828 table_attach_label_l(table, _("Nodes:"), 0, 1, 1, 2);
829 table_attach_int(table, dirty.nodes.total, 1, 2, 1, 2);
830 table_attach_int(table, dirty.nodes.new, 2, 3, 1, 2);
831 table_attach_int(table, dirty.nodes.dirty, 3, 4, 1, 2);
832 table_attach_int(table, dirty.nodes.deleted, 4, 5, 1, 2);
833
834 table_attach_label_l(table, _("Ways:"), 0, 1, 2, 3);
835 table_attach_int(table, dirty.ways.total, 1, 2, 2, 3);
836 table_attach_int(table, dirty.ways.new, 2, 3, 2, 3);
837 table_attach_int(table, dirty.ways.dirty, 3, 4, 2, 3);
838 table_attach_int(table, dirty.ways.deleted, 4, 5, 2, 3);
839
840 table_attach_label_l(table, _("Relations:"), 0, 1, 3, 4);
841 table_attach_int(table, dirty.relations.total, 1, 2, 3, 4);
842 table_attach_int(table, dirty.relations.new, 2, 3, 3, 4);
843 table_attach_int(table, dirty.relations.dirty, 3, 4, 3, 4);
844 table_attach_int(table, dirty.relations.deleted, 4, 5, 3, 4);
845
846 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
847
848 /* ------------------------------------------------------ */
849
850 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
851 gtk_hseparator_new());
852
853 /* ------- add username and password entries ------------ */
854
855 table = gtk_table_new(2, 2, FALSE);
856 table_attach_label_l(table, _("Username:"), 0, 1, 0, 1);
857 GtkWidget *uentry = gtk_entry_new();
858 HILDON_ENTRY_NO_AUTOCAP(uentry);
859 gtk_entry_set_text(GTK_ENTRY(uentry), appdata->settings->username);
860 gtk_table_attach_defaults(GTK_TABLE(table), uentry, 1, 2, 0, 1);
861 table_attach_label_l(table, _("Password:"), 0, 1, 1, 2);
862 GtkWidget *pentry = gtk_entry_new();
863 HILDON_ENTRY_NO_AUTOCAP(pentry);
864 gtk_entry_set_text(GTK_ENTRY(pentry), appdata->settings->password);
865 gtk_entry_set_visibility(GTK_ENTRY(pentry), FALSE);
866 gtk_table_attach_defaults(GTK_TABLE(table), pentry, 1, 2, 1, 2);
867 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
868
869 GtkWidget *scrolled_win = gtk_scrolled_window_new(NULL, NULL);
870 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
871 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
872 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_win),
873 GTK_SHADOW_IN);
874
875 GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
876 gtk_text_buffer_set_text(buffer, _("Please add a comment"), -1);
877
878 /* disable ok button until user edited the comment */
879 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
880 GTK_RESPONSE_ACCEPT, FALSE);
881
882 g_signal_connect(G_OBJECT(buffer), "changed",
883 G_CALLBACK(callback_buffer_modified), dialog);
884
885 GtkWidget *view = gtk_text_view_new_with_buffer(buffer);
886 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
887 gtk_text_view_set_editable(GTK_TEXT_VIEW(view), TRUE);
888 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 2 );
889 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 2 );
890
891 g_object_set_data(G_OBJECT(view), "first_click", GINT_TO_POINTER(TRUE));
892 g_signal_connect(G_OBJECT(view), "focus-in-event",
893 G_CALLBACK(cb_focus_in), buffer);
894
895
896 gtk_container_add(GTK_CONTAINER(scrolled_win), view);
897
898 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
899 scrolled_win);
900 gtk_widget_show_all(dialog);
901
902 if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
903 printf("upload cancelled\n");
904 gtk_widget_destroy(dialog);
905 return;
906 }
907
908 printf("clicked ok\n");
909
910 /* retrieve username and password */
911 if(appdata->settings->username)
912 g_free(appdata->settings->username);
913 appdata->settings->username =
914 g_strdup(gtk_entry_get_text(GTK_ENTRY(uentry)));
915
916 if(appdata->settings->password)
917 g_free(appdata->settings->password);
918 appdata->settings->password =
919 g_strdup(gtk_entry_get_text(GTK_ENTRY(pentry)));
920
921 /* osm upload itself also has a gui */
922 osm_upload_context_t *context = g_new0(osm_upload_context_t, 1);
923 context->appdata = appdata;
924 context->osm = osm;
925 context->project = project;
926
927 /* add proxy settings if required */
928 if(appdata->settings)
929 context->proxy = appdata->settings->proxy;
930
931 /* fetch comment from dialog */
932 GtkTextIter start, end;
933 gtk_text_buffer_get_start_iter(buffer, &start);
934 gtk_text_buffer_get_end_iter(buffer, &end);
935 char *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
936 if(text) context->comment = g_strdup(text);
937
938 gtk_widget_destroy(dialog);
939 project_save(GTK_WIDGET(appdata->window), project);
940
941 context->dialog =
942 misc_dialog_new(MISC_DIALOG_LARGE,_("Uploading"),
943 GTK_WINDOW(appdata->window),
944 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
945
946 gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
947 GTK_RESPONSE_CLOSE, FALSE);
948
949 /* ------- main ui elelent is this text view --------------- */
950
951 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
952 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
953 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
954
955 context->log.buffer = gtk_text_buffer_new(NULL);
956
957 context->log.view = gtk_text_view_new_with_buffer(context->log.buffer);
958 gtk_text_view_set_editable(GTK_TEXT_VIEW(context->log.view), FALSE);
959 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(context->log.view), FALSE);
960 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(context->log.view), GTK_WRAP_WORD);
961
962 gtk_container_add(GTK_CONTAINER(scrolled_window), context->log.view);
963 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
964 GTK_SHADOW_IN);
965
966 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
967 scrolled_window);
968 gtk_widget_show_all(context->dialog);
969
970 /* server url should not end with a slash */
971 if(project->server[strlen(project->server)-1] == '/') {
972 printf("removing trailing slash\n");
973 project->server[strlen(project->server)-1] = 0;
974 }
975
976 appendf(&context->log, NULL, _("Log generated by %s v%s using API 0.6\n"),
977 PACKAGE, VERSION);
978 appendf(&context->log, NULL, _("User comment: %s\n"), context->comment);
979
980 g_assert(project->server);
981
982 /* check if server name contains string "0.5" and adjust it */
983 if(strstr(project->server, "0.5") != NULL) {
984 strstr(project->server, "0.5")[2] = '6';
985
986 appendf(&context->log, NULL, _("Adjusting server name to v0.6\n"));
987 }
988
989 appendf(&context->log, NULL, _("Uploading to %s\n"), project->server);
990
991 /* create a new changeset */
992 if(osm_create_changeset(context)) {
993 /* check for dirty entries */
994 appendf(&context->log, NULL, _("Uploading nodes:\n"));
995 osm_upload_nodes(context);
996 appendf(&context->log, NULL, _("Uploading ways:\n"));
997 osm_upload_ways(context);
998 appendf(&context->log, NULL, _("Uploading relations:\n"));
999 osm_upload_relations(context);
1000 appendf(&context->log, NULL, _("Deleting relations:\n"));
1001 osm_delete_relations(context);
1002 appendf(&context->log, NULL, _("Deleting ways:\n"));
1003 osm_delete_ways(context);
1004 appendf(&context->log, NULL, _("Deleting nodes:\n"));
1005 osm_delete_nodes(context);
1006
1007 /* close changeset */
1008 osm_close_changeset(context);
1009 }
1010
1011 appendf(&context->log, NULL, _("Upload done.\n"));
1012
1013 gboolean reload_map = FALSE;
1014 if(project->data_dirty) {
1015 appendf(&context->log, NULL, _("Server data has been modified.\n"));
1016 appendf(&context->log, NULL, _("Downloading updated osm data ...\n"));
1017
1018 if(osm_download(context->dialog, appdata->settings, project)) {
1019 appendf(&context->log, NULL, _("Download successful!\n"));
1020 appendf(&context->log, NULL, _("The map will be reloaded.\n"));
1021 project->data_dirty = FALSE;
1022 reload_map = TRUE;
1023 } else
1024 appendf(&context->log, NULL, _("Download failed!\n"));
1025
1026 project_save(context->dialog, project);
1027
1028 if(reload_map) {
1029 /* this kind of rather brute force reload is useful as the moment */
1030 /* after the upload is a nice moment to bring everything in sync again. */
1031 /* we basically restart the entire map with fresh data from the server */
1032 /* and the diff will hopefully be empty (if the upload was successful) */
1033
1034 appendf(&context->log, NULL, _("Reloading map ...\n"));
1035
1036 if(!diff_is_clean(appdata->osm, FALSE)) {
1037 appendf(&context->log, COLOR_ERR, _("*** DIFF IS NOT CLEAN ***\n"));
1038 appendf(&context->log, COLOR_ERR, _("Something went wrong during upload,\n"));
1039 appendf(&context->log, COLOR_ERR, _("proceed with care!\n"));
1040 }
1041
1042 /* redraw the entire map by destroying all map items and redrawing them */
1043 appendf(&context->log, NULL, _("Cleaning up ...\n"));
1044 diff_save(appdata->project, appdata->osm);
1045 map_clear(appdata, MAP_LAYER_OBJECTS_ONLY);
1046 osm_free(&appdata->icon, appdata->osm);
1047
1048 appendf(&context->log, NULL, _("Loading OSM ...\n"));
1049 appdata->osm = osm_parse(appdata->project->path, appdata->project->osm);
1050 appendf(&context->log, NULL, _("Applying diff ...\n"));
1051 diff_restore(appdata, appdata->project, appdata->osm);
1052 appendf(&context->log, NULL, _("Painting ...\n"));
1053 map_paint(appdata);
1054 appendf(&context->log, NULL, _("Done!\n"));
1055 }
1056 }
1057
1058 /* tell the user that he can stop waiting ... */
1059 appendf(&context->log, NULL, _("Process finished.\n"));
1060
1061 gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
1062 GTK_RESPONSE_CLOSE, TRUE);
1063
1064 gtk_dialog_run(GTK_DIALOG(context->dialog));
1065 gtk_widget_destroy(context->dialog);
1066
1067 if(context->comment) g_free(context->comment);
1068 g_free(context);
1069 }
1070