OSDN Git Service

[Refactor] #37353 world_type を world.c/h へ移動。
[hengband/hengband.git] / src / report.c
1 /*!
2  * @file report.c
3  * @brief スコアサーバ転送機能の実装
4  * @date 2014/07/14
5  * @author Hengband Team
6  */
7
8 #define _GNU_SOURCE /*!< 未使用*/
9 #include "angband.h"
10 #include "util.h"
11
12 #include "floor.h"
13 #include "player-status.h"
14 #include "files.h"
15 #include "world.h"
16
17 #ifdef WORLD_SCORE
18
19 #include <stdio.h>
20 #include <stdarg.h>
21 #include <ctype.h>
22 #include <string.h>
23
24 #if defined(WINDOWS)
25 #include <winsock.h>
26 #elif defined(MACINTOSH)
27 #include <OpenTransport.h>
28 #include <OpenTptInternet.h>
29 #else
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <netdb.h>
34 #include <sys/time.h>
35
36 #include <setjmp.h>
37 #include <signal.h>
38 #endif
39
40 /*
41  * internet resource value
42  */
43 #define HTTP_PROXY ""                   /*!< デフォルトのプロキシURL / Default proxy url */
44 #define HTTP_PROXY_PORT 0               /*!< デフォルトのプロキシポート / Default proxy port */
45 #define HTTP_TIMEOUT    20              /*!< デフォルトのタイムアウト時間(秒) / Timeout length (second) */
46 #define SCORE_SERVER "hengband.osdn.jp" /*!< デフォルトのスコアサーバURL / Default score server url */
47 #define SCORE_PORT 80                   /*!< デフォルトのスコアサーバポート / Default score server port */
48
49 #ifdef JP
50 #define SCORE_PATH "http://hengband.osdn.jp/score/register_score.php" /*!< スコア開示URL */
51 #else
52 #define SCORE_PATH "http://moon.kmc.gr.jp/hengband/hengscore-en/score.cgi" /*!< スコア開示URL */
53 #endif
54
55 /* for debug */
56 #if 0
57 #define SCORE_PATH "http://moon.kmc.gr.jp/hengband/scoretest/score.cgi" /*!< スコア開示URL */
58 #endif
59
60 /*
61  * simple buffer library
62  */
63 typedef struct {
64         size_t max_size;
65         size_t size;
66         char *data;
67 } BUF;
68
69 #define BUFSIZE (65536) /*!< スコアサーバ転送バッファサイズ */
70
71 /*!
72  * @brief 転送用バッファの確保
73  * @return 確保したバッファの参照ポインタ
74  */
75 static BUF* buf_new(void)
76 {
77         BUF *p;
78
79         if ((p = malloc(sizeof(BUF))) == NULL)
80                 return NULL;
81
82         p->size = 0;
83         p->max_size = BUFSIZE;
84         if ((p->data = malloc(BUFSIZE)) == NULL)
85         {
86                 free(p);
87                 return NULL;
88         }
89         return p;
90 }
91
92 /*!
93  * @brief 転送用バッファの解放
94  * @param b 解放するバッファの参照ポインタ
95  */
96 static void buf_delete(BUF *b)
97 {
98         free(b->data);
99         free(b);
100 }
101
102 /*!
103  * @brief 転送用バッファにデータを追加する
104  * @param buf 追加先バッファの参照ポインタ
105  * @param data 追加元データ
106  * @param size 追加サイズ
107  * @return 追加後のバッファ容量
108  */
109 static int buf_append(BUF *buf, concptr data, size_t size)
110 {
111         while (buf->size + size > buf->max_size)
112         {
113                 char *tmp;
114                 if ((tmp = malloc(buf->max_size * 2)) == NULL) return -1;
115
116                 memcpy(tmp, buf->data, buf->max_size);
117                 free(buf->data);
118
119                 buf->data = tmp;
120
121                 buf->max_size *= 2;
122         }
123         memcpy(buf->data + buf->size, data, size);
124         buf->size += size;
125
126         return buf->size;
127 }
128
129 /*!
130  * @brief 転送用バッファにフォーマット指定した文字列データを追加する
131  * @param buf 追加先バッファの参照ポインタ
132  * @param fmt 文字列フォーマット
133  * @return 追加後のバッファ容量
134  */
135 static int buf_sprintf(BUF *buf, concptr fmt, ...)
136 {
137         int             ret;
138         char    tmpbuf[8192];
139         va_list ap;
140
141         va_start(ap, fmt);
142 #if defined(HAVE_VSNPRINTF)
143         ret = vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, ap);
144 #else
145         ret = vsprintf(tmpbuf, fmt, ap);
146 #endif
147         va_end(ap);
148
149         if (ret < 0) return -1;
150
151 #if ('\r' == 0x0a && '\n' == 0x0d)
152         {
153                 /*
154                  * Originally '\r'= CR (= 0x0d) and '\n'= LF (= 0x0a)
155                  * But for MPW (Macintosh Programers Workbench), these
156                  * are reversed so that '\r'=LF and '\n'=CR unless the
157                  * -noMapCR option is not defined.
158                  *
159                  * We need to swap back these here since the score
160                  * dump text should be written using LF as the end of
161                  * line.
162                  */
163                 char *ptr;
164                 for (ptr = tmpbuf; *ptr; ptr++)
165                 {
166                         if (0x0d == *ptr) *ptr = 0x0a;
167                 }
168         }
169 #endif
170
171         ret = buf_append(buf, tmpbuf, strlen(tmpbuf));
172
173         return ret;
174 }
175
176 #if 0
177 static int buf_read(BUF *buf, int fd)
178 {
179         int len;
180 #ifndef MACINTOSH
181         char tmp[BUFSIZE];
182 #else
183         char *tmp;
184         
185         tmp = calloc( BUFSIZE , sizeof(char) );
186 #endif
187
188         while ((len = read(fd, tmp, BUFSIZE)) > 0)
189                 buf_append(buf, tmp, len);
190
191         return buf->size;
192 }
193 #endif
194
195 #if 0
196 static int buf_write(BUF *buf, int fd)
197 {
198         write(fd, buf->data, buf->size);
199
200         return buf->size;
201 }
202
203 static int buf_search(BUF *buf, concptr str)
204 {
205         char *ret;
206
207         ret = my_strstr(buf->data, str);
208
209         if (!ret) return -1;
210
211         return ret - buf->data;
212 }
213
214 static BUF * buf_subbuf(BUF *buf, int pos1, size_t sz)
215 {
216         BUF *ret;
217
218         if (pos1 < 0) return NULL;
219
220         ret = buf_new();
221
222         if (sz <= 0) sz = buf->size - pos1;
223
224         buf_append(ret, buf->data + pos1, sz);
225
226         return ret;
227 }
228 #endif
229
230 /*!
231  * @brief HTTPによるダンプ内容伝送
232  * @param sd ソケットID
233  * @param url 伝送先URL
234  * @param buf 伝送内容バッファ
235  * @return なし
236  */
237 static bool http_post(int sd, concptr url, BUF *buf)
238 {
239         BUF *output;
240         char response_buf[1024] = "";
241         concptr HTTP_RESPONSE_CODE_OK = "HTTP/1.1 200 OK";
242
243         output = buf_new();
244         buf_sprintf(output, "POST %s HTTP/1.0\r\n", url);
245         buf_sprintf(output, "User-Agent: Hengband %d.%d.%d\r\n",
246                     FAKE_VER_MAJOR-10, FAKE_VER_MINOR, FAKE_VER_PATCH);
247
248         buf_sprintf(output, "Content-Length: %d\r\n", buf->size);
249         buf_sprintf(output, "Content-Encoding: binary\r\n");
250 #ifdef JP
251 #ifdef SJIS
252         buf_sprintf(output, "Content-Type: text/plain; charset=SHIFT_JIS\r\n");
253 #endif
254 #ifdef EUC
255         buf_sprintf(output, "Content-Type: text/plain; charset=EUC-JP\r\n");
256 #endif
257 #else
258         buf_sprintf(output, "Content-Type: text/plain; charset=ASCII\r\n");
259 #endif
260         buf_sprintf(output, "\r\n");
261         buf_append(output, buf->data, buf->size);
262
263         soc_write(sd, output->data, output->size);
264
265         soc_read(sd, response_buf, sizeof(response_buf));
266
267         return strncmp(response_buf, HTTP_RESPONSE_CODE_OK, strlen(HTTP_RESPONSE_CODE_OK)) == 0;
268 }
269
270 /*!
271  * @brief キャラクタダンプを作って BUFに保存
272  * @param dumpbuf 伝送内容バッファ
273  * @return エラーコード
274  */
275 static errr make_dump(BUF* dumpbuf)
276 {
277         char            buf[1024];
278         FILE *fff;
279         GAME_TEXT file_name[1024];
280
281         /* Open a new file */
282         fff = my_fopen_temp(file_name, 1024);
283         if (!fff)
284         {
285 #ifdef JP
286                 msg_format("一時ファイル %s を作成できませんでした。", file_name);
287 #else
288                 msg_format("Failed to create temporary file %s.", file_name);
289 #endif
290                 msg_print(NULL);
291                 return 1;
292         }
293
294         /* 一旦一時ファイルを作る。通常のダンプ出力と共通化するため。 */
295         (void)make_character_dump(fff);
296         my_fclose(fff);
297
298         /* Open for read */
299         fff = my_fopen(file_name, "r");
300
301         while (fgets(buf, 1024, fff))
302         {
303                 (void)buf_sprintf(dumpbuf, "%s", buf);
304         }
305         my_fclose(fff);
306         fd_kill(file_name);
307
308         /* Success */
309         return (0);
310 }
311
312 /*!
313  * @brief スクリーンダンプを作成する/ Make screen dump to buffer
314  * @return 作成したスクリーンダンプの参照ポインタ
315  */
316 concptr make_screen_dump(void)
317 {
318         BUF *screen_buf;
319         int y, x, i;
320         concptr ret;
321
322         TERM_COLOR a = 0, old_a = 0;
323         SYMBOL_CODE c = ' ';
324
325         static concptr html_head[] = {
326                 "<html>\n<body text=\"#ffffff\" bgcolor=\"#000000\">\n",
327                 "<pre>",
328                 0,
329         };
330         static concptr html_foot[] = {
331                 "</pre>\n",
332                 "</body>\n</html>\n",
333                 0,
334         };
335
336         bool old_use_graphics = use_graphics;
337
338         int wid, hgt;
339
340         Term_get_size(&wid, &hgt);
341
342         /* Alloc buffer */
343         screen_buf = buf_new();
344         if (screen_buf == NULL) return (NULL);
345
346         if (old_use_graphics)
347         {
348                 /* Clear -more- prompt first */
349                 msg_print(NULL);
350
351                 use_graphics = FALSE;
352                 reset_visuals();
353
354                 p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY);
355                 handle_stuff();
356         }
357
358         for (i = 0; html_head[i]; i++)
359                 buf_sprintf(screen_buf, html_head[i]);
360
361         /* Dump the screen */
362         for (y = 0; y < hgt; y++)
363         {
364                 /* Start the row */
365                 if (y != 0)
366                         buf_sprintf(screen_buf, "\n");
367
368                 /* Dump each row */
369                 for (x = 0; x < wid - 1; x++)
370                 {
371                         int rv, gv, bv;
372                         concptr cc = NULL;
373                         /* Get the attr/char */
374                         (void)(Term_what(x, y, &a, &c));
375
376                         switch (c)
377                         {
378                         case '&': cc = "&amp;"; break;
379                         case '<': cc = "&lt;"; break;
380                         case '>': cc = "&gt;"; break;
381                         case '"': cc = "&quot;"; break;
382                         case '\'': cc = "&#39;"; break;
383 #ifdef WINDOWS
384                         case 0x1f: c = '.'; break;
385                         case 0x7f: c = (a == 0x09) ? '%' : '#'; break;
386 #endif
387                         }
388
389                         a = a & 0x0F;
390                         if ((y == 0 && x == 0) || a != old_a) {
391                                 rv = angband_color_table[a][1];
392                                 gv = angband_color_table[a][2];
393                                 bv = angband_color_table[a][3];
394                                 buf_sprintf(screen_buf, "%s<font color=\"#%02x%02x%02x\">", 
395                                             ((y == 0 && x == 0) ? "" : "</font>"), rv, gv, bv);
396                                 old_a = a;
397                         }
398                         if (cc)
399                                 buf_sprintf(screen_buf, "%s", cc);
400                         else
401                                 buf_sprintf(screen_buf, "%c", c);
402                 }
403         }
404         buf_sprintf(screen_buf, "</font>");
405
406         for (i = 0; html_foot[i]; i++)
407                 buf_sprintf(screen_buf, html_foot[i]);
408
409         /* Screen dump size is too big ? */
410         if (screen_buf->size + 1> SCREEN_BUF_MAX_SIZE)
411         {
412                 ret = NULL;
413         }
414         else
415         {
416                 /* Terminate string */
417                 buf_append(screen_buf, "", 1);
418
419                 ret = string_make(screen_buf->data);
420         }
421
422         /* Free buffer */
423         buf_delete(screen_buf);
424
425         if (old_use_graphics)
426         {
427                 use_graphics = TRUE;
428                 reset_visuals();
429
430                 p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIPPY);
431                 handle_stuff();
432         }
433
434         return ret;
435 }
436
437 /*!
438  * @brief スコア転送処理のメインルーチン
439  * @return エラーコード
440  */
441 errr report_score(void)
442 {
443 #ifdef MACINTOSH
444         OSStatus err;
445 #else
446         errr err = 0;
447 #endif
448
449 #ifdef WINDOWS
450         WSADATA wsaData;
451         WORD wVersionRequested =(WORD) (( 1) |  ( 1 << 8));
452 #endif
453
454         BUF *score;
455         int sd;
456         char seikakutmp[128];
457
458         score = buf_new();
459
460 #ifdef JP
461         sprintf(seikakutmp, "%s%s", ap_ptr->title, (ap_ptr->no ? "の" : ""));
462 #else
463         sprintf(seikakutmp, "%s ", ap_ptr->title);
464 #endif
465
466         buf_sprintf(score, "name: %s\n", p_ptr->name);
467 #ifdef JP
468         buf_sprintf(score, "version: 変愚蛮怒 %d.%d.%d\n",
469                     FAKE_VER_MAJOR-10, FAKE_VER_MINOR, FAKE_VER_PATCH);
470 #else
471         buf_sprintf(score, "version: Hengband %d.%d.%d\n",
472                     FAKE_VER_MAJOR-10, FAKE_VER_MINOR, FAKE_VER_PATCH);
473 #endif
474         buf_sprintf(score, "score: %d\n", calc_score());
475         buf_sprintf(score, "level: %d\n", p_ptr->lev);
476         buf_sprintf(score, "depth: %d\n", current_floor_ptr->dun_level);
477         buf_sprintf(score, "maxlv: %d\n", p_ptr->max_plv);
478         buf_sprintf(score, "maxdp: %d\n", max_dlv[DUNGEON_ANGBAND]);
479         buf_sprintf(score, "au: %d\n", p_ptr->au);
480         buf_sprintf(score, "turns: %d\n", turn_real(current_world_ptr->game_turn));
481         buf_sprintf(score, "sex: %d\n", p_ptr->psex);
482         buf_sprintf(score, "race: %s\n", rp_ptr->title);
483         buf_sprintf(score, "class: %s\n", cp_ptr->title);
484         buf_sprintf(score, "seikaku: %s\n", seikakutmp);
485         buf_sprintf(score, "realm1: %s\n", realm_names[p_ptr->realm1]);
486         buf_sprintf(score, "realm2: %s\n", realm_names[p_ptr->realm2]);
487         buf_sprintf(score, "killer: %s\n", p_ptr->died_from);
488         buf_sprintf(score, "-----charcter dump-----\n");
489
490         make_dump(score);
491
492         if (screen_dump)
493         {
494                 buf_sprintf(score, "-----screen shot-----\n");
495                 buf_append(score, screen_dump, strlen(screen_dump));
496         }
497         
498 #ifdef WINDOWS
499         if (WSAStartup(wVersionRequested, &wsaData))
500         {
501                 msg_print("Report: WSAStartup failed.");
502                 goto report_end;
503         }
504 #endif
505
506 #ifdef MACINTOSH
507 #if TARGET_API_MAC_CARBON
508         err = InitOpenTransportInContext(kInitOTForApplicationMask, NULL);
509 #else
510         err = InitOpenTransport();
511 #endif
512         if (err != noErr)
513         {
514                 msg_print("Report: OpenTransport failed.");
515                 return 1;
516         }
517 #endif
518
519         Term_clear();
520
521         while (1)
522         {
523                 char buff[160];
524 #ifdef JP
525                 prt("接続中...", 0, 0);
526 #else
527                 prt("connecting...", 0, 0);
528 #endif
529                 Term_fresh();
530                 
531                 /* プロキシを設定する */
532                 set_proxy(HTTP_PROXY, HTTP_PROXY_PORT);
533
534                 /* Connect to the score server */
535                 sd = connect_server(HTTP_TIMEOUT, SCORE_SERVER, SCORE_PORT);
536
537
538                 if (sd < 0) {
539 #ifdef JP
540                         sprintf(buff, "スコア・サーバへの接続に失敗しました。(%s)", soc_err());
541 #else
542                         sprintf(buff, "Failed to connect to the score server.(%s)", soc_err());
543 #endif
544                         prt(buff, 0, 0);
545                         (void)inkey();
546
547 #ifdef JP
548                         if (!get_check_strict("もう一度接続を試みますか? ", CHECK_NO_HISTORY))
549 #else
550                         if (!get_check_strict("Try again? ", CHECK_NO_HISTORY))
551 #endif
552                         {
553                                 err = 1;
554                                 goto report_end;
555                         }
556
557                         continue;
558                 }
559
560 #ifdef JP
561                 prt("スコア送信中...", 0, 0);
562 #else
563                 prt("Sending the score...", 0, 0);
564 #endif
565                 Term_fresh();
566
567                 if (!http_post(sd, SCORE_PATH, score)) {
568                         disconnect_server(sd);
569 #ifdef JP
570                         sprintf(buff, "スコア・サーバへの送信に失敗しました。");
571 #else
572                         sprintf(buff, "Failed to send to the score server.");
573 #endif
574                         prt(buff, 0, 0);
575                         (void)inkey();
576
577 #ifdef JP
578                         if (!get_check_strict("もう一度接続を試みますか? ", CHECK_NO_HISTORY))
579 #else
580                         if (!get_check_strict("Try again? ", CHECK_NO_HISTORY))
581 #endif
582                         {
583                                 err = 1;
584                                 goto report_end;
585                         }
586
587                         continue;
588                 }
589
590                 disconnect_server(sd);
591                 break;
592         }
593
594  report_end:
595 #ifdef WINDOWS
596         WSACleanup();
597 #endif
598
599 #ifdef MACINTOSH
600 #if TARGET_API_MAC_CARBON
601         CloseOpenTransportInContext(NULL);
602 #else
603         CloseOpenTransport();
604 #endif
605 #endif
606
607         return err;
608 }
609
610 #endif /* WORLD_SCORE */