Index: branches/mark/udpmcast/udpmcast/udpmcast.py |
— | — | @@ -0,0 +1,95 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +# |
| 4 | +# udpcast.py |
| 5 | +# application level udp multicaster/multiplexer |
| 6 | +# Written on 2005/04/03 by Mark Bergsma <mark@nedworks.org> |
| 7 | +# |
| 8 | +# $Id$ |
| 9 | + |
| 10 | +import socket, getopt, sys, os |
| 11 | + |
| 12 | +debugging = False |
| 13 | + |
| 14 | +def debug(msg): |
| 15 | + global debugging |
| 16 | + if debugging: |
| 17 | + print msg; |
| 18 | + |
| 19 | +def multicast_diagrams(sock, addresses): |
| 20 | + portnr = sock.getsockname()[1]; |
| 21 | + |
| 22 | + while 1: |
| 23 | + diagram = sock.recv(1500) |
| 24 | + if not diagram: break |
| 25 | + for addr in addresses: |
| 26 | + try: |
| 27 | + sock.sendto(diagram, 0, (addr, portnr)) |
| 28 | + debug('Sent diagram to '+addr+' port '+portnr) |
| 29 | + except socket.error: |
| 30 | + debug('Error while sending diagram to '+addr) |
| 31 | + pass |
| 32 | + |
| 33 | +def join_multicast_group(sock, multicast_group): |
| 34 | + import struct |
| 35 | + |
| 36 | + ip_mreq = struct.pack('!4sl', socket.inet_aton(multicast_group), |
| 37 | + socket.INADDR_ANY) |
| 38 | + sock.setsockopt(socket.IPPROTO_IP, |
| 39 | + socket.IP_ADD_MEMBERSHIP, |
| 40 | + ip_mreq) |
| 41 | + |
| 42 | +def print_help(): |
| 43 | + print 'Usage:\n\tudpmcast [ options ] { addresses }\n' |
| 44 | + print 'Options:' |
| 45 | + print '\t-d\tFork into the background (become a daemon)' |
| 46 | + print '\t-p {portnr}\tUDP port number to listen on (default is 4827)' |
| 47 | + print '\t-j {multicast address}\tMulticast group to join on startup' |
| 48 | + print '\t-v\tBe more verbose' |
| 49 | + |
| 50 | +if __name__ == '__main__': |
| 51 | + host = '' |
| 52 | + portnr = 4827 |
| 53 | + multicast_group = None |
| 54 | + daemon = False |
| 55 | + opts = 'dhj:p:v' |
| 56 | + |
| 57 | + # Parse options |
| 58 | + options, arguments = getopt.getopt(sys.argv[1:], opts) |
| 59 | + if len(arguments) == 0: |
| 60 | + print_help() |
| 61 | + sys.exit() |
| 62 | + else: |
| 63 | + for option, value in options: |
| 64 | + if option == '-j': |
| 65 | + multicast_group = value |
| 66 | + elif option == '-p': |
| 67 | + portnr = int(value) |
| 68 | + elif option == '-h': |
| 69 | + print_help() |
| 70 | + sys.exit() |
| 71 | + elif option == '-d': |
| 72 | + daemon = True |
| 73 | + elif option == '-v': |
| 74 | + debugging = True |
| 75 | + |
| 76 | + try: |
| 77 | + # Open the UDP socket |
| 78 | + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
| 79 | + sock.bind((host, portnr)) |
| 80 | + |
| 81 | + # Join a multicast group if requested |
| 82 | + if multicast_group != None: |
| 83 | + debug('Joining multicast group ' + multicast_group) |
| 84 | + join_multicast_group(sock, multicast_group) |
| 85 | + |
| 86 | + # Become a daemon |
| 87 | + if daemon and os.fork(): |
| 88 | + sys.exit() |
| 89 | + |
| 90 | + # Multiplex everything that comes in |
| 91 | + multicast_diagrams(sock, arguments) |
| 92 | + except socket.error, msg: |
| 93 | + print msg[1]; |
| 94 | + except KeyboardInterrupt: |
| 95 | + pass |
| 96 | + |
Property changes on: branches/mark/udpmcast/udpmcast/udpmcast.py |
___________________________________________________________________ |
Added: svn:keywords |
1 | 97 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 98 | + native |
Added: svn:executable |
3 | 99 | + * |
Index: branches/mark/udpmcast/rcdumper/rcdumper.cc |
— | — | @@ -0,0 +1,715 @@ |
| 2 | +/* |
| 3 | + * $Header$ |
| 4 | + * |
| 5 | + * Realtime recent changes feed. |
| 6 | + * This code is in the public domain. |
| 7 | + */ |
| 8 | + |
| 9 | +#include <sys/types.h> |
| 10 | +#include <sys/socket.h> |
| 11 | + |
| 12 | +#include <netinet/in.h> |
| 13 | + |
| 14 | +#include <arpa/inet.h> |
| 15 | + |
| 16 | +#include <map> |
| 17 | +#include <list> |
| 18 | +#include <set> |
| 19 | +#include <vector> |
| 20 | +#include <cstdio> |
| 21 | +#include <cstdlib> |
| 22 | +#include <cstring> |
| 23 | +#include <queue> |
| 24 | +#include <string> |
| 25 | +#include <iostream> |
| 26 | +#include <fstream> |
| 27 | +#include <sstream> |
| 28 | +#include <ctime> |
| 29 | + |
| 30 | +#include <unistd.h> |
| 31 | + |
| 32 | +#include <mysql.h> |
| 33 | + |
| 34 | +std::string messages_en[] = { |
| 35 | + "Show bots", |
| 36 | + "Hide bots", |
| 37 | + "Show logged in users", |
| 38 | + "Hide logged in users", |
| 39 | + "Show minor edits", |
| 40 | + "Hide minor edits", |
| 41 | + "Feedback", |
| 42 | + "Key: N = new; B = bot; m = minor; 0-9 = number of reverts last 24 hrs", |
| 43 | + "diff", |
| 44 | + "Talk", |
| 45 | + "hist", |
| 46 | + "contribs", |
| 47 | + "moved to", |
| 48 | + "N", "B", "m" |
| 49 | +}; |
| 50 | + |
| 51 | +std::string messages_sv[] = { |
| 52 | + "Visa robotredigeringar", |
| 53 | + "Göm robotredigeringar", |
| 54 | + "Visa inloggade användare", |
| 55 | + "Göm inloggade användare", |
| 56 | + "Visa mindre redigeringar", |
| 57 | + "Göm mindre redigeringar", |
| 58 | + "Respons", |
| 59 | + "Key: N = ny; B = robot; m = mindre redigering; 0-9 = antal återställningar de senaste 24 timmarna", |
| 60 | + "skillnad", |
| 61 | + "Diskussion", |
| 62 | + "historik", |
| 63 | + "contribs", |
| 64 | + "moved to", |
| 65 | + "N", "B", "m" |
| 66 | +}; |
| 67 | + |
| 68 | +std::string messages_fi[] = { |
| 69 | + "Näytä botit", |
| 70 | + "Piilota botit", |
| 71 | + "Näytä kirjautuneiden käyttäjien muokkaukset", |
| 72 | + "Piilota kirjautuneiden käyttäjien muokkaukset", |
| 73 | + "Näytä pienet muokkaukset", |
| 74 | + "Piilota pienet muokkaukset", |
| 75 | + "Palaute", |
| 76 | + "Tiedot: U = uusi, B = botti, p = pieni muutos, 0-9 = palautukset 24 tunnin sisällä.", |
| 77 | + "ero", |
| 78 | + "Keskustelu", |
| 79 | + "historia", |
| 80 | + "contribs", |
| 81 | + "moved to", |
| 82 | + "U", "B", "p" |
| 83 | +}; |
| 84 | +std::string messages_nl[] = { |
| 85 | + "Toon robots", |
| 86 | + "Verberg robots", |
| 87 | + "Toon aangemelde gebruikers", |
| 88 | + "Verberg aangemelde gebruikers", |
| 89 | + "Toon kleine wijzigingen", |
| 90 | + "Verberg kleine wijzigingen", |
| 91 | + "Reageer", |
| 92 | + "Legende: N = nieuw, B = robot, K = kleine wijziging, 0-9 = aantal herstellingen in de laatste 24 uur.", |
| 93 | + "wijz", |
| 94 | + "Overleg", |
| 95 | + "gesch", |
| 96 | + "contribs", |
| 97 | + "moved to", |
| 98 | + "N", "B", "K" |
| 99 | +}; |
| 100 | + |
| 101 | +std::string messages_fr[] = { |
| 102 | + "montrer robots", |
| 103 | + "cacher robots", |
| 104 | + "montrer utilisateurs enregistrés", |
| 105 | + "cacher utilisateurs enregistrés", |
| 106 | + "montrer modifications mineures", |
| 107 | + "cacher modifications mineures", |
| 108 | + "Commentaires", |
| 109 | + "Légende: N = nouveau, B = Bot, m = Modifications mineures, 0-9 = nombre de réversions dans les dernières 24 heures", |
| 110 | + "diff", |
| 111 | + "Discuter", |
| 112 | + "hist", |
| 113 | + "contribs", |
| 114 | + "moved to", |
| 115 | + "N", "B", "m" |
| 116 | +}; |
| 117 | +std::string messages_it[] = { |
| 118 | + "Mostra i bot", |
| 119 | + "Nascondi i bot", |
| 120 | + "Mostra gli utenti connessi", |
| 121 | + "Nascondi gli utenti connessi", |
| 122 | + "Mostra le modifiche minori", |
| 123 | + "Nascondi le modifiche minori", |
| 124 | + "Feedback", |
| 125 | + "Key: N = nuovo; B = bot; m = modifica minore; 0-9 = numbero di rollback nelle 24 ore", |
| 126 | + "diff", |
| 127 | + "Discussioni", |
| 128 | + "cron", |
| 129 | + "contribs", |
| 130 | + "moved to", |
| 131 | + "N", "B", "m" |
| 132 | +}; |
| 133 | + |
| 134 | +std::string messages_de[] = { |
| 135 | + "Bots zeigen", |
| 136 | + "Bots verstecken", |
| 137 | + "Angemeldete Benutzer zeigen", |
| 138 | + "Angemeldete Benutzer verstecken", |
| 139 | + "Kleine Änderungen zeigen", |
| 140 | + "Kleine Änderungen verstecken", |
| 141 | + "Feedback", |
| 142 | + "Legende: N = neu, B = Bot, K = kleine Änderung, 0-9 = Zahl der Reverts in den letzten 24 Std", |
| 143 | + "Unterschiede", |
| 144 | + "Diskussion", |
| 145 | + "Versionen", |
| 146 | + "Benutzerbeiträge", |
| 147 | + "verschoben nach", |
| 148 | + "N", "B", "K" |
| 149 | +}; |
| 150 | + |
| 151 | +std::string messages_ja[] = { |
| 152 | + "ボットを表示する", |
| 153 | + "ボットを隠す", |
| 154 | + "ログインユーザを表示する", |
| 155 | + "ログインユーザを隠す", |
| 156 | + "細部の編集を表示する", |
| 157 | + "細部の編集を隠す", |
| 158 | + "フィードバックを書く", |
| 159 | + "記号: N = 新しい記事; B = ボット; m = 細部の編集; 0-9 最近24時間のリバート数", |
| 160 | + "差分", |
| 161 | + "ノート", |
| 162 | + "履歴", |
| 163 | + "投稿記録", |
| 164 | + "moved to", |
| 165 | + "N", "B", "m" |
| 166 | +}; |
| 167 | + |
| 168 | +std::string *messages = messages_en; |
| 169 | + |
| 170 | +#define M_SHOW_BOTS 0 |
| 171 | +#define M_HIDE_BOTS 1 |
| 172 | +#define M_SHOW_LOGGED 2 |
| 173 | +#define M_HIDE_LOGGED 3 |
| 174 | +#define M_SHOW_MINOR 4 |
| 175 | +#define M_HIDE_MINOR 5 |
| 176 | +#define M_FEEDBACK 6 |
| 177 | +#define M_KEY 7 |
| 178 | +#define M_DIFF 8 |
| 179 | +#define M_TALK 9 |
| 180 | +#define M_HIST 10 |
| 181 | +#define M_CONTRIBS 11 |
| 182 | +#define M_MOVED_TO 12 |
| 183 | +#define M_NEW 13 |
| 184 | +#define M_BOT 14 |
| 185 | +#define M_MINOR 15 |
| 186 | + |
| 187 | + |
| 188 | +std::string db; |
| 189 | +MYSQL mysql; |
| 190 | + |
| 191 | +template<class to, class from> |
| 192 | +to lexical_cast(from const& f) |
| 193 | +{ |
| 194 | + std::stringstream ss; |
| 195 | + to t; |
| 196 | + ss << f; |
| 197 | + ss >> t; |
| 198 | + return t; |
| 199 | +} |
| 200 | + |
| 201 | +std::string spaces(std::string const& s) { |
| 202 | + std::string res; |
| 203 | + for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) |
| 204 | + switch(*i) { |
| 205 | + case '_': res += " "; break; |
| 206 | + default: res += *i; break; |
| 207 | + } |
| 208 | + return res; |
| 209 | +} |
| 210 | + |
| 211 | +std::string underscores(std::string const& s) { |
| 212 | + std::string res; |
| 213 | + for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) |
| 214 | + switch(*i) { |
| 215 | + case ' ': res += "_"; break; |
| 216 | + default: res += *i; break; |
| 217 | + } |
| 218 | + return res; |
| 219 | +} |
| 220 | + |
| 221 | +std::string sqlsafe(std::string const& s) { |
| 222 | + std::string res; |
| 223 | + for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) |
| 224 | + switch(*i) { |
| 225 | + case '\\': res += "\\\\"; break; |
| 226 | + case '\'': res += "\\'"; break; |
| 227 | + case '"': res += "\\\""; break; |
| 228 | + default: res += *i; break; |
| 229 | + } |
| 230 | + return res; |
| 231 | +} |
| 232 | + |
| 233 | +void |
| 234 | +mysql_query_ordie(MYSQL* mysql, std::string const& query) |
| 235 | +{ |
| 236 | + int i = mysql_query(mysql, query.c_str()); |
| 237 | + if (i) { |
| 238 | + printf("mysql query failed: %s\n", mysql_error(mysql)); |
| 239 | + exit(8); |
| 240 | + } |
| 241 | +} |
| 242 | + |
| 243 | +std::string namespaces[] = { |
| 244 | + "", |
| 245 | + "Talk:", |
| 246 | + "User:", |
| 247 | + "User_talk:", |
| 248 | + "Wikipedia:", |
| 249 | + "Wikipedia_talk:", |
| 250 | + "Image:", |
| 251 | + "Image_talk:", |
| 252 | + "MediaWiki:", |
| 253 | + "MediaWiki_talk:", |
| 254 | + "Template:", |
| 255 | + "Template_talk:", |
| 256 | + "Help:", |
| 257 | + "Help_talk:", |
| 258 | + "Category:", |
| 259 | + "Category_talk:" |
| 260 | +}; |
| 261 | + |
| 262 | +std::string ns2name(int ns) { |
| 263 | + if (ns < -2 || ns > 15) return "Unknown namespace:"; |
| 264 | + mysql_query_ordie(&mysql, "SELECT ns_name FROM katesdb.ns_name WHERE ns_num = " + lexical_cast<std::string>(ns) |
| 265 | + + " AND ns_db = '" + sqlsafe(db) + "'"); |
| 266 | + MYSQL_RES *res = mysql_store_result(&mysql); |
| 267 | + MYSQL_ROW arow; |
| 268 | + if (arow = mysql_fetch_row(res)) |
| 269 | + return mysql_free_result(res), arow[0]; |
| 270 | + mysql_free_result(res); |
| 271 | + if (ns < 0) { |
| 272 | + if (ns == -1) return "Special:"; |
| 273 | + else if (ns == -2) return "Media:"; |
| 274 | + else return "[Unknown namespace]:"; |
| 275 | + } |
| 276 | + return namespaces[ns]; |
| 277 | +} |
| 278 | + |
| 279 | +int name2ns(std::string const& name) { |
| 280 | + if (name == "") return 0; |
| 281 | + mysql_query_ordie(&mysql, "SELECT ns_num FROM katesdb.ns_name WHERE ns_name = '" + sqlsafe(name) |
| 282 | + + "' AND ns_db = '" + sqlsafe(db) + "'"); |
| 283 | + MYSQL_RES *res = mysql_store_result(&mysql); |
| 284 | + MYSQL_ROW arow; |
| 285 | + if (arow = mysql_fetch_row(res)) |
| 286 | + return mysql_free_result(res), lexical_cast<int>(arow[0]); |
| 287 | + mysql_free_result(res); |
| 288 | + for (unsigned int i = 0; i < sizeof namespaces / sizeof *namespaces; ++i) |
| 289 | + if (name + ":" == namespaces[i]) return i; |
| 290 | + return 0; |
| 291 | +} |
| 292 | + |
| 293 | +std::string fmtdate(std::string const& date) { |
| 294 | + return date.substr(8, 2) + ":" + date.substr(10, 2) + ":" + date.substr(12, 2); |
| 295 | +} |
| 296 | + |
| 297 | +std::string htmlsafe(std::string const& s) { |
| 298 | + std::string res; |
| 299 | + for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) { |
| 300 | + switch(*i) { |
| 301 | + case '&': res += "&"; break; |
| 302 | + case '<': res += "<"; break; |
| 303 | + case '>': res += ">"; break; |
| 304 | + case '"': res += """; break; |
| 305 | + case '\'': res += "'"; break; |
| 306 | + case '?': res += "?"; break; |
| 307 | + default: res += *i; break; |
| 308 | + } |
| 309 | + } |
| 310 | + return res; |
| 311 | +} |
| 312 | + |
| 313 | +std::string urlsafe(std::string const& s) { |
| 314 | + std::string res; |
| 315 | + for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) { |
| 316 | + if ((*i >= 'a' && *i <= 'z') || (*i >= 'A' && *i <= 'Z') || (*i >= '0' && *i <= '9') || strchr("/:.", *i)) |
| 317 | + res += *i; |
| 318 | + else { |
| 319 | + res += '%'; |
| 320 | + char *s; asprintf(&s, "%2x", (unsigned int)(unsigned char)*i); |
| 321 | + res += s; |
| 322 | + std::free(s); |
| 323 | + } |
| 324 | + } |
| 325 | + return res; |
| 326 | +} |
| 327 | + |
| 328 | + |
| 329 | +std::string |
| 330 | +getdbname() |
| 331 | +{ |
| 332 | + char *e; |
| 333 | + if ((e = getenv("QUERY_STRING")) == NULL) return "enwiki"; |
| 334 | + std::string s = e; |
| 335 | + std::string d = "enwiki", lang; |
| 336 | + std::string thispar; |
| 337 | + std::string *thisval; |
| 338 | + /* foo=bar&baz=quux */ |
| 339 | + int doing = 0; /* 0 = name / 1 = par */ |
| 340 | + for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) { |
| 341 | + switch (*i) { |
| 342 | + case '=': |
| 343 | + doing = 1; |
| 344 | + if (thispar == "d") thisval = &d; |
| 345 | + else thisval = ⟨ |
| 346 | + *thisval = ""; |
| 347 | + thispar = ""; |
| 348 | + break; |
| 349 | + case '&': |
| 350 | + doing = 0; |
| 351 | + thispar = ""; |
| 352 | + break; |
| 353 | + default: |
| 354 | + if (doing == 0) { |
| 355 | + if (thispar.size() > 255) { |
| 356 | + std::cout << "Content-Type: text/plain\r\n\r\n...\r\n"; |
| 357 | + std::exit(0); |
| 358 | + } |
| 359 | + thispar += *i; |
| 360 | + } else { |
| 361 | + if (thisval->size() > 255) { |
| 362 | + std::cout << "Content-Type: text/plain\r\n\r\n...\r\n"; |
| 363 | + std::exit(0); |
| 364 | + } |
| 365 | + *thisval += *i; |
| 366 | + } |
| 367 | + } |
| 368 | + } |
| 369 | + //if (s.substr(0, 2) == "d=") |
| 370 | + // d = s.substr(2); |
| 371 | + //else |
| 372 | + // return "enwiki"; |
| 373 | + if (lang == "ja") messages = messages_ja; |
| 374 | + else if (lang == "de") messages = messages_de; |
| 375 | + else if (lang == "it") messages = messages_it; |
| 376 | + else if (lang == "fi") messages = messages_fi; |
| 377 | + else if (lang == "nl") messages = messages_nl; |
| 378 | + else if (lang == "fr") messages = messages_fr; |
| 379 | + else if (lang == "sv") messages = messages_sv; |
| 380 | + std::ifstream f("/home/wikipedia/common/all.dblist"); |
| 381 | + while (std::getline(f, s)) |
| 382 | + if (s == d) |
| 383 | + return d; |
| 384 | + return ""; |
| 385 | +} |
| 386 | + |
| 387 | +std::string latin1dbs[] = { |
| 388 | + "enwiki", "dawiki", "svwiki", "nlwiki" |
| 389 | +}; |
| 390 | + |
| 391 | +std::string |
| 392 | +getencoding(std::string const& db) { |
| 393 | + for (int i = 0; i < 4; ++i) |
| 394 | + if (db == latin1dbs[i]) |
| 395 | + return "ISO_8859-1"; |
| 396 | + return "UTF-8"; |
| 397 | +} |
| 398 | + |
| 399 | +std::string |
| 400 | +maybeutf8(std::string const& s) { |
| 401 | + if (getencoding(db) == "UTF-8") |
| 402 | + return s; |
| 403 | + char const *q = s.c_str(), *t = q; |
| 404 | + size_t qs = s.size(); |
| 405 | + static bool init = false; |
| 406 | + static iconv_t ic; |
| 407 | + if (!init) { |
| 408 | + ++init; |
| 409 | + ic = iconv_open("UTF-8", getencoding(db).c_str()); |
| 410 | + } |
| 411 | + size_t rs = s.size() * 3; |
| 412 | + char *r = new char[rs + 1], *z = r; |
| 413 | + size_t i = iconv(ic, const_cast<char**>(&t), &qs, &z, &rs); |
| 414 | + std::string p(r, z); |
| 415 | + delete[] r; |
| 416 | + if (i == (size_t)-1) { |
| 417 | + return "!Conversion error: " + std::string(strerror(errno)); |
| 418 | + } |
| 419 | + return p; |
| 420 | +} |
| 421 | + |
| 422 | +char const* projects[][2] = { |
| 423 | + {"wiki", "wikipedia.org"}, |
| 424 | + {"wiktionary", "wiktionary.org"}, |
| 425 | + {"wikibooks", "wikibooks.org"}, |
| 426 | + {"wikiquote", "wikiquote.org"}, |
| 427 | + {"wikinews", "wikinews.org"} |
| 428 | +}; |
| 429 | + |
| 430 | +std::string |
| 431 | +gethost(std::string const& db) |
| 432 | +{ |
| 433 | + if (db == "commonswiki") return "commons.wikimedia.org"; |
| 434 | + for (unsigned int i = 0; i < sizeof projects / sizeof *projects; ++i) |
| 435 | + if (db.substr(db.size() - strlen(projects[i][0])) == projects[i][0]) |
| 436 | + return db.substr(0, db.size() - strlen(projects[i][0])) + "." + projects[i][1]; |
| 437 | + return "unknown"; |
| 438 | +} |
| 439 | + |
| 440 | +std::string sane(std::string const& article) { |
| 441 | + return article[0] == ':' ? article.substr(1) : article; |
| 442 | +} |
| 443 | + |
| 444 | +bool articleexists(int ns, std::string article) |
| 445 | +{ |
| 446 | + if (ns < 0) return true; |
| 447 | + mysql_query_ordie(&mysql, "SELECT 1 FROM cur WHERE cur_namespace="+lexical_cast<std::string>(ns)+ |
| 448 | + " AND cur_title='"+sqlsafe(underscores(sane(article)))+"'"); |
| 449 | + MYSQL_RES *res = mysql_store_result(&mysql); |
| 450 | + if (mysql_fetch_row(res)) |
| 451 | + return mysql_free_result(res), true; |
| 452 | + mysql_free_result(res); |
| 453 | + return false; |
| 454 | +} |
| 455 | + |
| 456 | +std::string |
| 457 | +fmtart(int ns, std::string link, std::string desc) |
| 458 | +{ |
| 459 | + link = sane(link); |
| 460 | + std::string clas = articleexists(ns, link) ? "article" : "new"; |
| 461 | + return "<a href='http://"+gethost(db)+"/wiki/"+urlsafe(ns2name(ns)+link)+"' class='"+clas+"'>"+ |
| 462 | + htmlsafe(desc.size()?desc:spaces(link))+"</a>"; |
| 463 | +} |
| 464 | + |
| 465 | +std::string |
| 466 | +link2url(std::string link, bool safe=true) |
| 467 | +{ |
| 468 | + link = sane(link); |
| 469 | + std::string art = link, desc; |
| 470 | + std::string::size_type i = art.find('|'); |
| 471 | + if (i != art.npos) { |
| 472 | + desc = spaces((i+1 == art.size() ? art : art.substr(i + 1))); |
| 473 | + art = art.substr(0, i); |
| 474 | + if (desc == "") desc = art; |
| 475 | + } else desc = art; |
| 476 | + std::string titlebit; |
| 477 | + int ns = 0; |
| 478 | + std::string q = sane(underscores(art)); |
| 479 | + i = q.find(':'); |
| 480 | + if (i != q.npos) |
| 481 | + ns = name2ns((i+1 == q.size() ? q : q.substr(0, i))); |
| 482 | + std::string clas = articleexists(ns, q.substr(i==q.npos?0:i+1)) ? "article" : "new"; |
| 483 | + return "<a href='http://"+gethost(db)+"/wiki/"+ |
| 484 | + (safe?urlsafe(q):q)+"' class='"+clas+"'>"+(safe?htmlsafe(desc):desc)+"</a>"; |
| 485 | +} |
| 486 | + |
| 487 | +std::string |
| 488 | +parsesummary(std::string s) |
| 489 | +{ |
| 490 | + std::string::size_type i,j; |
| 491 | + while ((i = s.find("[[")) != s.npos) { |
| 492 | + if ((j = s.find("]]", i)) == s.npos) break; |
| 493 | + s = s.substr(0, i) + link2url(s.substr(i + 2, j - i - 2), false) + s.substr(j + 2); |
| 494 | + } |
| 495 | + while (i = s.find("/* ") != s.npos) { |
| 496 | + if ((j = s.find(" */", i)) == s.npos) break; |
| 497 | + s = s.substr(0, i - 1) + "<span style='color: gray'>" + s.substr(i + 2, j - i - 2) + "</span>" |
| 498 | + + s.substr(j + 3); |
| 499 | + } |
| 500 | + return s; |
| 501 | +} |
| 502 | + |
| 503 | +std::string |
| 504 | +whatwasthedateyesterday() { |
| 505 | + std::time_t t = std::time(NULL); |
| 506 | + t -= 86400; |
| 507 | + struct tm *gt = gmtime(&t); |
| 508 | + char b[256]; |
| 509 | + std::strftime(b, sizeof b, "%Y%m%d%H%M%S", gt); |
| 510 | + return b; |
| 511 | +} |
| 512 | + |
| 513 | +template<typename it> |
| 514 | +it next(it i) |
| 515 | +{ |
| 516 | + return ++i; |
| 517 | +} |
| 518 | + |
| 519 | +std::string |
| 520 | +mkrvtxt(std::set<std::string> const& rvers) |
| 521 | +{ |
| 522 | + if (rvers.empty()) return ""; |
| 523 | + std::string s = " ["; |
| 524 | + for (std::set<std::string>::const_iterator it = rvers.begin(), end = rvers.end(); it != end; ++it) { |
| 525 | + s += fmtart(2, *it, *it); |
| 526 | + if (next(it) != end) |
| 527 | + s += ", "; |
| 528 | + } |
| 529 | + return s + "]"; |
| 530 | +} |
| 531 | + |
| 532 | +int |
| 533 | +main(int argc, char *argv[]) |
| 534 | +{ |
| 535 | + db = getdbname(); |
| 536 | + |
| 537 | + if (db.empty()) { |
| 538 | + std::cout << "Content-Type: text/plain\r\n\r\nincorrect dbname predicts future losses\n"; |
| 539 | + return 0; |
| 540 | + } |
| 541 | + |
| 542 | + mysql_init(&mysql); |
| 543 | + mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "rcdumper"); |
| 544 | + |
| 545 | + std::string dbhost, dbuser, dbpass, s; |
| 546 | + std::vector<std::string> v; |
| 547 | + std::ifstream f("/home/kate/dbconfig"); |
| 548 | + while (std::getline(f, s)) { |
| 549 | + v.push_back(s); |
| 550 | + } |
| 551 | + f.close(); |
| 552 | + if (v.size() < 3) { |
| 553 | + std::cerr << "Content-Type: text/plain\r\n\r\nsorry, broken\r\n"; |
| 554 | + std::exit(0); |
| 555 | + } |
| 556 | + dbhost = v[0]; |
| 557 | + dbuser = v[1]; |
| 558 | + dbpass = v[2]; |
| 559 | + |
| 560 | + if (!mysql_real_connect(&mysql, dbhost.c_str(), dbuser.c_str(), dbpass.c_str(), db.c_str(), 0, NULL, 0)) { |
| 561 | + printf("mysql connect error: %s\n", mysql_error(&mysql)); |
| 562 | + return 1; |
| 563 | + } |
| 564 | + |
| 565 | + //std::cout << "HTTP/1.0 200 OK\r\n" << std::flush; |
| 566 | + std::cout << "Content-type: text/html; charset=UTF-8\r\n" << std::flush; |
| 567 | + std::cout << "\r\n" << std::flush; |
| 568 | + std::cout << "<html><head><title>live recent changes feed</title>" |
| 569 | +"<style type='text/css'>" |
| 570 | +"body { padding: 0em; margin: 0em; }" |
| 571 | +"a { text-decoration: none; }" |
| 572 | +"a.new { color: red; }" |
| 573 | +"</style>" |
| 574 | +"<script language='javascript'>" |
| 575 | +"var showbots = false;" |
| 576 | +"var showminor = true;" |
| 577 | +"var showlogin = true;" |
| 578 | +"function toggleminor() {" |
| 579 | +" showminor = !showminor;" |
| 580 | +" v = document.getElementById('minortog');" |
| 581 | +" if (showminor) v.innerHTML = '"+messages[M_HIDE_MINOR]+"';" |
| 582 | +" else v.innerHTML = '"+messages[M_SHOW_MINOR]+"';" |
| 583 | +"}" |
| 584 | +"function togglelogin() {" |
| 585 | +" showlogin = !showlogin;" |
| 586 | +" v = document.getElementById('logintog');" |
| 587 | +" if (showlogin) v.innerHTML = '"+messages[M_HIDE_LOGGED]+"';" |
| 588 | +" else v.innerHTML = '"+messages[M_SHOW_LOGGED]+"';" |
| 589 | +"}" |
| 590 | +"function togglebots() {" |
| 591 | +" showbots = !showbots;" |
| 592 | +" v = document.getElementById('bottog');" |
| 593 | +" if (showbots) v.innerHTML = '"+messages[M_HIDE_BOTS]+"';" |
| 594 | +" else v.innerHTML = '"+messages[M_SHOW_BOTS]+"';" |
| 595 | +"}" |
| 596 | +"</script>" |
| 597 | +"</head><body><ul>\n" |
| 598 | +"<div style='width: 90%; height: 8%; padding-left: 2em; padding-right: 2em; border-bottom: solid 1px black; text-align: center'>" |
| 599 | +"<a id='bottog' href='javascript:togglebots()'>"+messages[M_SHOW_BOTS]+"</a> " |
| 600 | +"| <a id='logintog' href='javascript:togglelogin()'>"+messages[M_HIDE_LOGGED]+"</a> " |
| 601 | +"| <a id='minortog' href='javascript:toggleminor()'>"+messages[M_HIDE_MINOR]+"</a>" |
| 602 | +"| <a id='feedback' href='http://meta.wikimedia.org/wiki/Realtime_recent_changes'>"+messages[M_FEEDBACK]+"</a>" |
| 603 | +"<br/>" +messages[M_KEY]+ "</div>" |
| 604 | +"<div style='padding-left: 2em; padding-right: 2em; height: 85%; overflow: scroll;' id='contentbit'>\n" |
| 605 | + << std::flush; |
| 606 | + |
| 607 | + mysql_query_ordie(&mysql, "SELECT MAX(rc_id) FROM recentchanges"); |
| 608 | + MYSQL_ROW arow; |
| 609 | + MYSQL_RES *res; |
| 610 | + res = mysql_store_result(&mysql); |
| 611 | + arow = mysql_fetch_row(res); |
| 612 | + std::string lastrcid = arow[0]; |
| 613 | + mysql_free_result(res); |
| 614 | + |
| 615 | + while (true) { |
| 616 | + mysql_query_ordie(&mysql, "SELECT rc_id, rc_timestamp, rc_user_text, rc_namespace, rc_title, " |
| 617 | + "rc_comment, rc_bot, rc_minor, rc_new, rc_user, rc_this_oldid, rc_cur_id, rc_moved_to_ns, " |
| 618 | + "rc_moved_to_title,cur_text " |
| 619 | + "FROM recentchanges LEFT OUTER JOIN cur ON cur_id=rc_cur_id WHERE rc_id > " + lastrcid + " ORDER BY rc_id"); |
| 620 | + res = mysql_store_result(&mysql); |
| 621 | + while (arow = mysql_fetch_row(res)) { |
| 622 | + std::string rc_timestamp = arow[1], |
| 623 | + rc_user_text = arow[2], |
| 624 | + rc_comment = arow[5], |
| 625 | + rc_cur_id = arow[11], |
| 626 | + rc_moved_title = arow[13] ? arow[13] : "", |
| 627 | + cur_text = arow[14] ? arow[14] : "", |
| 628 | + rc_title = arow[4]; |
| 629 | + int rc_namespace = atoi(arow[3]), |
| 630 | + rc_bot = atoi(arow[6]), |
| 631 | + rc_new = atoi(arow[8]), |
| 632 | + rc_user = atoi(arow[9]), |
| 633 | + rc_oldid = atoi(arow[10]), |
| 634 | + rc_minor_edit = atoi(arow[7]), |
| 635 | + rc_moved_ns = arow[12] ? atoi(arow[12]) : 0; |
| 636 | + mysql_query_ordie(&mysql, "SELECT COUNT(*) FROM hashs WHERE hs_nstitle=MD5(CONCAT('" |
| 637 | + + lexical_cast<std::string>(rc_namespace) + "','+','" |
| 638 | + + sqlsafe(rc_title) + "')) AND hs_hash=MD5('" |
| 639 | + + sqlsafe(cur_text) + "') AND hs_timestamp > " + whatwasthedateyesterday()); |
| 640 | + MYSQL_RES *res2 = mysql_store_result(&mysql); |
| 641 | + MYSQL_ROW brow = mysql_fetch_row(res2); |
| 642 | + int rc_reverts = atoi(brow[0]); |
| 643 | + if (rc_reverts > 9) rc_reverts = 9; |
| 644 | + mysql_free_result(res2); |
| 645 | + std::set<std::string> reverters; |
| 646 | + mysql_query_ordie(&mysql, "SELECT DISTINCT hs_user_text FROM hashs WHERE hs_nstitle=MD5(CONCAT('" |
| 647 | + + lexical_cast<std::string>(rc_namespace) + "','+','" |
| 648 | + + sqlsafe(rc_title) + "')) AND hs_hash=MD5('" |
| 649 | + + sqlsafe(cur_text) + "') AND hs_timestamp > " + whatwasthedateyesterday()); |
| 650 | + res2 = mysql_store_result(&mysql); |
| 651 | + while (brow = mysql_fetch_row(res2)) { |
| 652 | + if (reverters.find(brow[0]) == reverters.end()) |
| 653 | + reverters.insert(brow[0]); |
| 654 | + } |
| 655 | + std::string reverter_text = mkrvtxt(reverters); |
| 656 | + mysql_free_result(res2); |
| 657 | + |
| 658 | + lastrcid = arow[0]; |
| 659 | + if (rc_bot) std::cout << |
| 660 | + "<script language='javascript'>" |
| 661 | + "if (showbots == false) document.write('<span style=\"display:none\">')</script>"; |
| 662 | + if (rc_minor_edit) std::cout << |
| 663 | + "<script language='javascript'>" |
| 664 | + "if (showminor == false) document.write('<span style=\"display:none\">')</script>"; |
| 665 | + if (rc_user != 0) std::cout << |
| 666 | + "<script language='javascript'>" |
| 667 | + "if (showlogin == false) document.write('<span style=\"display:none\">')</script>"; |
| 668 | + std::cout << fmtdate(rc_timestamp) + " " |
| 669 | + "<tt>" |
| 670 | + + std::string(rc_minor_edit ? messages[M_MINOR] : " ") |
| 671 | + + std::string(rc_bot ? messages[M_BOT] : " ") |
| 672 | + + std::string(rc_new ? messages[M_NEW] : " ") |
| 673 | + + std::string(rc_reverts ? lexical_cast<std::string>(rc_reverts) : " ") |
| 674 | + + " </tt>" |
| 675 | + "<a href='http://"+gethost(db)+"/wiki/" |
| 676 | + + urlsafe(ns2name(rc_namespace) + rc_title) |
| 677 | + + "'>" + spaces(maybeutf8(ns2name(rc_namespace) + htmlsafe(rc_title))) + "</a>" |
| 678 | + + std::string(" (") |
| 679 | + + (rc_moved_title.size() |
| 680 | + ? (messages[M_MOVED_TO] + " " + |
| 681 | + fmtart(rc_moved_ns, rc_moved_title, maybeutf8(ns2name(rc_moved_ns) + rc_moved_title))) |
| 682 | + : (!rc_new ? |
| 683 | + ("<a href='http://"+gethost(db)+"/wiki/" |
| 684 | + + urlsafe(ns2name(rc_namespace) + rc_title) |
| 685 | + + "?curid="+rc_cur_id+"&diff=0'>" + messages[M_DIFF] + "</a>") |
| 686 | + :(messages[M_DIFF])) |
| 687 | + ) |
| 688 | + + "; " |
| 689 | + "<a href='http://"+gethost(db)+"/wiki/" |
| 690 | + + urlsafe(ns2name(rc_namespace) + rc_title) |
| 691 | + + "?action=history'>"+messages[M_HIST]+"</a>) . . " |
| 692 | + + fmtart(2, rc_user_text, maybeutf8(rc_user_text)) |
| 693 | + + " (" + fmtart(3, rc_user_text, messages[M_TALK]) + " | " |
| 694 | + + fmtart(-1, "Contributions/" + underscores(rc_user_text), messages[M_CONTRIBS]) + ")" |
| 695 | + + ((rc_moved_title.empty() && !rc_comment.empty()) |
| 696 | + ? " (" + parsesummary(htmlsafe(rc_comment)) + ")" |
| 697 | + : std::string() |
| 698 | + ) |
| 699 | + + maybeutf8(reverter_text) |
| 700 | + + "<br/>\n" << std::endl << std::flush; |
| 701 | + if (rc_user != 0) std::cout << |
| 702 | + "<script language='javascript'>if (showlogin == false) document.write('</span>')</script>\n"; |
| 703 | + if (rc_minor_edit) std::cout << |
| 704 | + "<script language='javascript'>if (showminor == false) document.write('</span>')</script>\n"; |
| 705 | + if (rc_bot) std::cout << |
| 706 | + "<script language='javascript'>if (showbots == false) document.write('</span>')</script>\n"; |
| 707 | + std::cout << |
| 708 | +"<script language='javascript'>" |
| 709 | +" var v = document.getElementById('contentbit');" |
| 710 | +" v.scrollTop = v.scrollTop + 100;" |
| 711 | +"</script>\n" << std::flush; |
| 712 | + } |
| 713 | + mysql_free_result(res); |
| 714 | + usleep(250000); |
| 715 | + } |
| 716 | +} |
Property changes on: branches/mark/udpmcast/rcdumper/rcdumper.cc |
___________________________________________________________________ |
Added: svn:keywords |
1 | 717 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 718 | + native |
Index: branches/mark/udpmcast/six_degrees/linksc.cc |
— | — | @@ -0,0 +1,61 @@ |
| 2 | +/* |
| 3 | + * $Header$ |
| 4 | + * |
| 5 | + * Links path finder client. |
| 6 | + * This source code is in the public domain. |
| 7 | + * |
| 8 | + */ |
| 9 | + |
| 10 | +#include <sys/types.h> |
| 11 | +#include <sys/socket.h> |
| 12 | + |
| 13 | +#include <netinet/in.h> |
| 14 | + |
| 15 | +#include <arpa/inet.h> |
| 16 | + |
| 17 | +#include <iostream> |
| 18 | +#include <cstdio> |
| 19 | +#include <cstdlib> |
| 20 | +#include <cstring> |
| 21 | +#include <string> |
| 22 | + |
| 23 | +#include <unistd.h> |
| 24 | + |
| 25 | +#define PORT 7584 |
| 26 | + |
| 27 | +int |
| 28 | +main(int argc, char *argv[]) |
| 29 | +{ |
| 30 | + std::string src, dst; |
| 31 | + std::getline(std::cin, src); |
| 32 | + std::getline(std::cin, dst); |
| 33 | + int s; |
| 34 | + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { |
| 35 | + printf("ERROR\nNO_CONNECT\n"); |
| 36 | + exit(8); |
| 37 | + } |
| 38 | + struct sockaddr_in addr; |
| 39 | + memset(&addr, 0, sizeof(addr)); |
| 40 | + addr.sin_family = AF_INET; |
| 41 | + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); |
| 42 | + addr.sin_port = htons(PORT); |
| 43 | + if (connect(s, (sockaddr *) &addr, sizeof(addr)) < 0) { |
| 44 | + printf("ERROR\nNO_CONNECT\n"); |
| 45 | + exit(8); |
| 46 | + } |
| 47 | + uint32_t i; |
| 48 | + i = src.size(); |
| 49 | + write(s, &i, sizeof(i)); |
| 50 | + write(s, src.data(), src.size()); |
| 51 | + i = dst.size(); |
| 52 | + write(s, &i, sizeof(i)); |
| 53 | + write(s, dst.data(), dst.size()); |
| 54 | + char buf[512]; |
| 55 | + int j; |
| 56 | + while ((j = read(s, buf, sizeof(buf))) > 0) { |
| 57 | + for (char *s = buf; s < buf + j; ++s) |
| 58 | + if (*s != 0) /* work around old linksd bug */ |
| 59 | + write(STDOUT_FILENO, s, 1); |
| 60 | + } |
| 61 | +} |
| 62 | + |
Property changes on: branches/mark/udpmcast/six_degrees/linksc.cc |
___________________________________________________________________ |
Added: svn:keywords |
1 | 63 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 64 | + native |
Index: branches/mark/udpmcast/six_degrees/linksd.cc |
— | — | @@ -0,0 +1,233 @@ |
| 2 | +/* |
| 3 | + * $Header$ |
| 4 | + * |
| 5 | + * Links path finder daemon. |
| 6 | + * This source code is in the public domain. |
| 7 | + * |
| 8 | + */ |
| 9 | + |
| 10 | +#include <sys/types.h> |
| 11 | +#include <sys/socket.h> |
| 12 | + |
| 13 | +#include <netinet/in.h> |
| 14 | + |
| 15 | +#include <arpa/inet.h> |
| 16 | + |
| 17 | +#include <map> |
| 18 | +#include <list> |
| 19 | +#include <set> |
| 20 | +#include <vector> |
| 21 | +#include <cstdio> |
| 22 | +#include <cstdlib> |
| 23 | +#include <cstring> |
| 24 | +#include <cassert> |
| 25 | +#include <queue> |
| 26 | +#include <string> |
| 27 | + |
| 28 | +#include <unistd.h> |
| 29 | + |
| 30 | +#include <mysql.h> |
| 31 | + |
| 32 | +#define PORT 7584 /* KT :-) */ |
| 33 | + |
| 34 | +#define dbhost "whatever" |
| 35 | +#define dbuser "whatever" |
| 36 | +#define dbpass "whatever" |
| 37 | + |
| 38 | +std::vector<std::string> names; |
| 39 | +std::map<std::string, int> ids; |
| 40 | + |
| 41 | +std::vector< std::vector< int > > adjacency; |
| 42 | +std::vector< int > back; |
| 43 | +std::deque< int > next; |
| 44 | + |
| 45 | +/* dijkstra implementation by zorbathutt@efnet #c++, not copyrighted */ |
| 46 | +std::vector< int > findPath( int src, int dst ) { |
| 47 | + back.clear(); |
| 48 | + back.resize( adjacency.size(), -1 ); |
| 49 | + next.clear(); |
| 50 | + back[ src ] = -2; |
| 51 | + next.push_back( src ); |
| 52 | + while( next.size() ) { |
| 53 | + int ts = next[ 0 ]; |
| 54 | + next.pop_front(); |
| 55 | + if( ts == dst ) { |
| 56 | + std::vector< int > path; |
| 57 | + path.push_back( dst ); |
| 58 | + int lastlink = back[ dst ]; |
| 59 | + while( lastlink != -2 ) { |
| 60 | + assert( lastlink != -1 ); |
| 61 | + path.push_back( lastlink ); |
| 62 | + lastlink = back[ lastlink ]; |
| 63 | + } |
| 64 | + reverse( path.begin(), path.end() ); |
| 65 | + return path; |
| 66 | + } |
| 67 | + for( int i = 0; i < adjacency[ ts ].size(); i++ ) { |
| 68 | + if( back[ adjacency[ ts ][ i ] ] == -1 ) { |
| 69 | + back[ adjacency[ ts ][ i ] ] = ts; |
| 70 | + next.push_back( adjacency[ ts ][ i ] ); |
| 71 | + } |
| 72 | + } |
| 73 | + } |
| 74 | + return std::vector< int >(); |
| 75 | +} |
| 76 | + |
| 77 | +void |
| 78 | +ioloop(void) |
| 79 | +{ |
| 80 | + int sfd; |
| 81 | + struct sockaddr_in servaddr, cliaddr; |
| 82 | + if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { |
| 83 | + perror("socket"); |
| 84 | + exit(8); |
| 85 | + } |
| 86 | + memset(&servaddr, 0, sizeof(servaddr)); |
| 87 | + servaddr.sin_family = AF_INET; |
| 88 | + servaddr.sin_addr.s_addr = htonl(INADDR_ANY); |
| 89 | + servaddr.sin_port = htons(PORT); |
| 90 | + int one = 1; |
| 91 | + setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); |
| 92 | + if (bind(sfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { |
| 93 | + perror("bind"); |
| 94 | + exit(8); |
| 95 | + } |
| 96 | + if (listen(sfd, 5) < 0) { |
| 97 | + perror("listen"); |
| 98 | + exit(8); |
| 99 | + } |
| 100 | + int cfd; |
| 101 | + socklen_t clilen = sizeof(cliaddr); |
| 102 | + while ((cfd = accept(sfd, (sockaddr *) &cliaddr, &clilen)) > 0) { |
| 103 | + std::string from, to; |
| 104 | + uint32_t len; |
| 105 | + if (read(cfd, &len, sizeof(len)) < 0) { |
| 106 | + perror("read"); |
| 107 | + close(cfd); |
| 108 | + continue; |
| 109 | + } |
| 110 | +#define nofrom "ERROR\nNO_FROM\n" |
| 111 | +#define noto "ERROR\nNO_TO\n" |
| 112 | + if (len > 255) { |
| 113 | + write(cfd, nofrom, sizeof(nofrom)); |
| 114 | + close(cfd); |
| 115 | + continue; |
| 116 | + } |
| 117 | + std::vector<u_char> b(len); |
| 118 | + if (read(cfd, &b[0], len) < 0) { |
| 119 | + perror("read"); |
| 120 | + close(cfd); |
| 121 | + continue; |
| 122 | + } |
| 123 | + from.assign(b.begin(), b.end()); |
| 124 | + if (read(cfd, &len, sizeof(len)) < 0) { |
| 125 | + perror("read"); |
| 126 | + close(cfd); |
| 127 | + continue; |
| 128 | + } |
| 129 | + if (len > 255) { |
| 130 | + write(cfd, noto, sizeof(noto)); |
| 131 | + close(cfd); |
| 132 | + continue; |
| 133 | + } |
| 134 | + b.resize(len); |
| 135 | + if (read(cfd, &b[0], len) < 0) { |
| 136 | + perror("read"); |
| 137 | + close(cfd); |
| 138 | + continue; |
| 139 | + } |
| 140 | + to.assign(b.begin(), b.end()); |
| 141 | + int fromid, toid; |
| 142 | + if (ids.find(from) == ids.end()) { |
| 143 | + write(cfd, nofrom, sizeof(nofrom)); |
| 144 | + close(cfd); |
| 145 | + continue; |
| 146 | + } |
| 147 | + fromid = ids[from]; |
| 148 | + if (ids.find(to) == ids.end()) { |
| 149 | + write(cfd, noto, sizeof(noto)); |
| 150 | + close(cfd); |
| 151 | + continue; |
| 152 | + } |
| 153 | + toid = ids[to]; |
| 154 | + std::vector<int> links = findPath(fromid, toid); |
| 155 | +#define ok "OK\n" |
| 156 | + write(cfd, ok, sizeof(ok)); |
| 157 | + for (std::vector<int>::const_iterator it = links.begin(), end = links.end(); it != end; ++it) |
| 158 | + { |
| 159 | + std::string s = names[*it] + '\n'; |
| 160 | + b.assign(s.begin(), s.end()); |
| 161 | + write(cfd, &b[0], b.size()); |
| 162 | + } |
| 163 | + close(cfd); |
| 164 | + } |
| 165 | + exit(0); |
| 166 | +} |
| 167 | + |
| 168 | + |
| 169 | +void |
| 170 | +mysql_query_ordie(MYSQL* mysql, char const *query) |
| 171 | +{ |
| 172 | + int i = mysql_query(mysql, query); |
| 173 | + if (i) { |
| 174 | + printf("mysql query failed: %s\n", mysql_error(mysql)); |
| 175 | + exit(8); |
| 176 | + } |
| 177 | +} |
| 178 | +int |
| 179 | +main(int argc, char *argv[]) |
| 180 | +{ |
| 181 | + MYSQL mysql; |
| 182 | + mysql_init(&mysql); |
| 183 | + mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "linksd"); |
| 184 | + |
| 185 | + if (!mysql_real_connect(&mysql, dbhost, dbuser, dbpass, argv[1], 0, NULL, 0)) { |
| 186 | + printf("mysql connect error: %s\n", mysql_error(&mysql)); |
| 187 | + return 1; |
| 188 | + } |
| 189 | + |
| 190 | + printf("retrieving links table...\n"); |
| 191 | + mysql_query_ordie(&mysql, "SELECT l_to, l_from FROM links"); |
| 192 | + MYSQL_RES *res = mysql_use_result(&mysql); |
| 193 | + |
| 194 | + MYSQL_ROW arow; |
| 195 | + while (arow = mysql_fetch_row(res)) { |
| 196 | + int l_from = atoi(arow[1]); |
| 197 | + int l_to = atoi(arow[0]); |
| 198 | + if (l_from >= adjacency.size()) |
| 199 | + adjacency.resize(l_from + 1); |
| 200 | + std::vector<int>& l = adjacency[l_from]; |
| 201 | + l.insert(l.end(), l_to); |
| 202 | + } |
| 203 | + mysql_free_result(res); |
| 204 | + |
| 205 | + printf("ok\n"); |
| 206 | + printf("retrieving titles...\n"); |
| 207 | + mysql_query_ordie(&mysql, "SELECT cur_title,cur_id FROM cur WHERE cur_namespace=0"); |
| 208 | + res = mysql_use_result(&mysql); |
| 209 | + while (arow = mysql_fetch_row(res)) { |
| 210 | + std::string title = arow[0]; |
| 211 | + int id = atoi(arow[1]); |
| 212 | + if (id >= names.size()) |
| 213 | + names.resize(id + 1); |
| 214 | + names[id] = title; |
| 215 | + ids[title] = id; |
| 216 | + } |
| 217 | + printf("ok, %d links, %d titles\n", adjacency.size(), names.size()); |
| 218 | + mysql_free_result(res); |
| 219 | + printf("filtering links...\n"); |
| 220 | + for (int i = 1; i < adjacency.size(); ++i) { |
| 221 | + if (i >= names.size() || names[i].empty()) { |
| 222 | + adjacency[i].clear(); |
| 223 | + continue; |
| 224 | + } |
| 225 | + for (std::vector<int>::iterator it = adjacency[i].begin(); it != adjacency[i].end();) |
| 226 | + if (*it >= names.size() || names[*it].empty()) |
| 227 | + it = adjacency[i].erase(it); |
| 228 | + else ++it; |
| 229 | + } |
| 230 | + printf("ok\n"); |
| 231 | + |
| 232 | + mysql_close(&mysql); |
| 233 | + ioloop(); |
| 234 | +} |
Property changes on: branches/mark/udpmcast/six_degrees/linksd.cc |
___________________________________________________________________ |
Added: svn:keywords |
1 | 235 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 236 | + native |
Index: branches/mark/udpmcast/six_degrees/six_degrees.py |
— | — | @@ -0,0 +1,225 @@ |
| 2 | +#! /usr/bin/python |
| 3 | +# |
| 4 | +# $Header$ |
| 5 | +# |
| 6 | +# Links path finder CGI client. |
| 7 | +# This source is in the public domain. |
| 8 | + |
| 9 | +import sys |
| 10 | +import cgi |
| 11 | +import os |
| 12 | + |
| 13 | +print "Content-Type: text/html; charset=iso-8859-1" |
| 14 | +print |
| 15 | + |
| 16 | +form = cgi.FieldStorage() |
| 17 | + |
| 18 | +fromval = '' |
| 19 | +toval = '' |
| 20 | + |
| 21 | +def safe(s): |
| 22 | + return cgi.escape(s).replace('"', "&dquot;").replace("'", """) |
| 23 | + |
| 24 | +if form.has_key('from'): |
| 25 | + fromval = safe(form['from'].value) |
| 26 | +if form.has_key('to'): |
| 27 | + toval = safe(form['to'].value) |
| 28 | + |
| 29 | +print """ |
| 30 | +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| 31 | +<html> |
| 32 | +<head> |
| 33 | +<title>six degrees of wikipedia</title> |
| 34 | +<style type="text/css"> |
| 35 | +body { |
| 36 | + background-color: white; |
| 37 | + padding-right: 0px; |
| 38 | + margin-right: 0px; |
| 39 | +} |
| 40 | +input { border: solid 1px blue; background: white; color: black; } |
| 41 | +div.error { |
| 42 | + display: inline; |
| 43 | + border: solid 1px #ff8888; |
| 44 | +} |
| 45 | +span.error { |
| 46 | + padding-left: 1em; |
| 47 | + padding-right: 1em; |
| 48 | + background-color: #ffdcdc; |
| 49 | + font-weight: bold; |
| 50 | + border-right: solid 1px #ff8888; |
| 51 | +} |
| 52 | +span.errtext { |
| 53 | + padding-left: 1em; |
| 54 | + padding-right: 1em; |
| 55 | +} |
| 56 | +div.result { |
| 57 | + border: solid 1px #0000ff; |
| 58 | + width: 50%; |
| 59 | + margin-left: auto; margin-right: auto; |
| 60 | + text-align: center; |
| 61 | +} |
| 62 | +div.answer { |
| 63 | + width: 100%; |
| 64 | + text-align: center; |
| 65 | + border-bottom: solid 1px #0000ff; |
| 66 | + background-color: #ddddff; |
| 67 | +} |
| 68 | +span.art { |
| 69 | +} |
| 70 | +a { |
| 71 | + color: #0000C0; |
| 72 | +} |
| 73 | +a:hover { |
| 74 | + text-decoration: underline; |
| 75 | +} |
| 76 | +</style> |
| 77 | +</head> |
| 78 | +<body> |
| 79 | +<div style="text-align: right; padding: 0px; margin: 0px"><img src="/~kate/6deg.png" alt="" /><br/></div> |
| 80 | +<div style="text-align: center"> |
| 81 | +<i> |
| 82 | +<a href="http://en.wikipedia.org/wiki/Iterative_deepening_depth-first_search">iterative deepening</a> |
| 83 | +<a href="http://en.wikipedia.org/Depth-first_search">depth first search</a> |
| 84 | +<a href="http://en.wikipedia.org/Shortest_path_problem">shortest path</a> |
| 85 | +query tool for <a href="http://en.wikipedia.org/wiki/Main_Page">Wikipedia</a>...</i><br/> |
| 86 | +<i>six degrees</i> finds the shortest path between any two Wikipedia articles in the |
| 87 | +main namespace using wiki links |
| 88 | +</div> |
| 89 | +<div style="padding-top: 35px;"> |
| 90 | +<form method="post" action="six_degrees"> |
| 91 | +<center> |
| 92 | +<strong>find path...</strong> |
| 93 | +from: <input type="text" name="from" value=\"""" + fromval + """\"/> |
| 94 | +to: <input type="text" name="to" value=\"""" + toval + """\" /> |
| 95 | +<input type="submit" value="go" /> |
| 96 | +</center> |
| 97 | +</form> |
| 98 | +""" |
| 99 | + |
| 100 | +def mklink(art): |
| 101 | + a2 = art.replace('_', ' ') |
| 102 | + return "<a href=\"http://en.wikipedia.org/wiki/" + safe(art) + "\">" + safe(a2) + "</a>" |
| 103 | + |
| 104 | +def chop(s): |
| 105 | + if s[-1:] == '\n': |
| 106 | + return s[:-1] |
| 107 | + else: |
| 108 | + return s |
| 109 | + |
| 110 | +def getrecent(): |
| 111 | + try: |
| 112 | + f = file("/home/kate/rcq", "r") |
| 113 | + except IOError: |
| 114 | + return '' |
| 115 | + i = [] |
| 116 | + while True: |
| 117 | + s,t,d = f.readline(), f.readline(), f.readline() |
| 118 | + s = safe(chop(s)) |
| 119 | + t = safe(chop(t)) |
| 120 | + |
| 121 | + if '' in [s,t,d]: |
| 122 | + break |
| 123 | + i = i + [[s,t,d]] |
| 124 | + i.reverse() |
| 125 | + s = '' |
| 126 | + for t in i[:10]: |
| 127 | + s = s + '<li>' + mklink(t[0]) + ' -> ' + mklink(t[1]) + ' (' + t[2] + ' hops)' |
| 128 | + return s |
| 129 | + |
| 130 | +footer = """ |
| 131 | +<div style="width: 50%; margin-left: auto; margin-right: auto; border-top: solid 1px black; margin-top: 3em"> |
| 132 | +<div style="text-align: center"> |
| 133 | +<strong>hints:</strong> |
| 134 | +</div> |
| 135 | +<ul> |
| 136 | +<li>redirects are searched as well as articles</li> |
| 137 | +<li>using a redirect as the target will generally produce an inferior result</li> |
| 138 | +<li>article names are case sensitive except for the first letter, which is always capital</li> |
| 139 | +</ul> |
| 140 | +</div> |
| 141 | +</div> |
| 142 | +""" |
| 143 | + |
| 144 | +rc = getrecent() |
| 145 | +if rc != '': |
| 146 | + footer = footer + """ |
| 147 | +<div style="width: 50%; margin-left: auto; margin-right: auto; border-top: solid 1px black; margin-top: 3em"> |
| 148 | +<div style="text-align: center"> |
| 149 | +<strong>recent queries:</strong> |
| 150 | +<ul>""" |
| 151 | + footer = footer + rc |
| 152 | + footer = footer + "</ul></div>" |
| 153 | + |
| 154 | +footer = footer + """ |
| 155 | +<hr/> |
| 156 | +kate's tools: |
| 157 | + <a href="count_edits">user edit counter</a> |
| 158 | +| <strong>six degrees of wikipedia</strong> |
| 159 | +| <a href="mailto:keturner@livejournal.com">send feedback...</a> |
| 160 | +</div></body></html>""" |
| 161 | + |
| 162 | +def error(text): |
| 163 | + print "<center><div class='error'><span class='error'>error:</span><span class='errtext'>%s</span></div></center>" % cgi.escape(text) |
| 164 | + print footer |
| 165 | + sys.exit() |
| 166 | + |
| 167 | +if not (form.has_key('from') and form.has_key('to')): |
| 168 | + print footer |
| 169 | + sys.exit() |
| 170 | + |
| 171 | +def cfirst(s): |
| 172 | + return s[0].upper() + s[1:] |
| 173 | +srcname,targname = cfirst(form['from'].value.replace(' ', '_')), cfirst(form['to'].value.replace(' ', '_')) |
| 174 | + |
| 175 | +if len(srcname) > 255: |
| 176 | + error("source article %s does not exist" % srcname) |
| 177 | +if len(targname) > 255: |
| 178 | + error("target article %s does not exist" % targname) |
| 179 | +def findlink(src, dst): |
| 180 | + (sin, sout) = os.popen2("/home/kate/linksc") |
| 181 | + sin.write(src + '\n') |
| 182 | + sin.write(dst + '\n') |
| 183 | + sin.flush() |
| 184 | + i = sout.readline() |
| 185 | + if i == "ERROR\n": |
| 186 | + err = sout.readline() |
| 187 | + if err == "NO_FROM\n": |
| 188 | + error("source article %s does not exist" % src) |
| 189 | + elif err == "NO_TO\n": |
| 190 | + error("target article %s does not exist" % dst) |
| 191 | + elif err == "NO_CONNECT\n": |
| 192 | + error("could not contact links server") |
| 193 | + else: |
| 194 | + error("unknown error") |
| 195 | + i = [] |
| 196 | + while True: |
| 197 | + nid = sout.readline() |
| 198 | + if nid == '': |
| 199 | + return i |
| 200 | + i = i + [nid] |
| 201 | + |
| 202 | +def mklink(art): |
| 203 | + a2 = art.replace('_', ' ') |
| 204 | + return "<a href=\"http://en.wikipedia.org/wiki/" + safe(art) + "\">" + cgi.escape(a2) + "</a>" |
| 205 | + |
| 206 | +def writelog(src, targ, degs): |
| 207 | + try: |
| 208 | + f = file("/home/kate/rcq", "a") |
| 209 | + f.write(src + '\n' + targ + '\n' + str(degs) + '\n') |
| 210 | + except IOError: |
| 211 | + return |
| 212 | + f.close() |
| 213 | + |
| 214 | +answer = findlink(srcname, targname) |
| 215 | +if answer != []: |
| 216 | + writelog(srcname, targname, len(answer)) |
| 217 | + print "<div class='result'>" |
| 218 | + print "<div class='answer'>%d degrees of separation</div>" % len(answer) |
| 219 | + print "<span class='art'>%s</span><br/>" % mklink(answer[0]), |
| 220 | + for i in answer[1:]: |
| 221 | + print "<span class='art'>%s</span><br/>" % mklink(i), |
| 222 | + #print "<span class='art'>%s</span>" % mklink(targname) |
| 223 | + print "</div>" |
| 224 | + print footer |
| 225 | + sys.exit() |
| 226 | +error("no route found after %d degrees..." % 10) |
Property changes on: branches/mark/udpmcast/six_degrees/six_degrees.py |
___________________________________________________________________ |
Added: svn:keywords |
1 | 227 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 228 | + native |
Added: svn:executable |
3 | 229 | + * |
Index: branches/mark/udpmcast/nodesync/nodesync.pl |
— | — | @@ -0,0 +1,23 @@ |
| 2 | +#! /usr/bin/perl -w |
| 3 | +# |
| 4 | +# $Header$ |
| 5 | +# Create dsh node_groups from nodes.list file. |
| 6 | +# |
| 7 | +# Permission is granted for anyone to use, modify and distribute this |
| 8 | +# source code for any purpose whatsoever, provided you balance a live |
| 9 | +# salmon on your head whilst doing so. |
| 10 | + |
| 11 | +use strict; |
| 12 | + |
| 13 | +unlink or die for glob "node_groups/*"; |
| 14 | + |
| 15 | +open LIST, "<nodes.list" or die; |
| 16 | +sub { |
| 17 | + my @nodes = split / /, shift; |
| 18 | + my $server = shift @nodes; |
| 19 | + sub { |
| 20 | + open my $h, ">>node_groups/". shift or die; |
| 21 | + print $h "$server\n"; |
| 22 | + }->($_) for @nodes; |
| 23 | +}->($_) for <LIST>; |
| 24 | + |
Property changes on: branches/mark/udpmcast/nodesync/nodesync.pl |
___________________________________________________________________ |
Added: svn:keywords |
1 | 25 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 26 | + native |
Added: svn:executable |
3 | 27 | + * |
Index: branches/mark/udpmcast/edit_counter/count_edits.py |
— | — | @@ -0,0 +1,141 @@ |
| 2 | +#! /usr/bin/env python |
| 3 | +# |
| 4 | +# $Header$ |
| 5 | +# |
| 6 | +# MediaWiki user edit counter. |
| 7 | +# This code is in the public domain. |
| 8 | + |
| 9 | +execfile("/home/kate/degree.cf", globals()); |
| 10 | + |
| 11 | +import sys |
| 12 | +import MySQLdb |
| 13 | + |
| 14 | +import cgi |
| 15 | +f = cgi.FieldStorage() |
| 16 | + |
| 17 | +def getdblist(): |
| 18 | + try: |
| 19 | + f = file("/home/wikipedia/common/all.dblist", "r") |
| 20 | + i = [] |
| 21 | + while True: |
| 22 | + s = f.readline() |
| 23 | + if s == '': |
| 24 | + return i |
| 25 | + s = s[:-1] |
| 26 | + i.append(s) |
| 27 | + return i |
| 28 | + except: |
| 29 | + return [] |
| 30 | + return [] |
| 31 | + |
| 32 | +if f.has_key('dbname') and f['dbname'].value in getdblist(): |
| 33 | + dbname = f['dbname'].value |
| 34 | + |
| 35 | +print "Content-Type: text/html; charset=iso-8859-1" |
| 36 | +print |
| 37 | +print """ |
| 38 | +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| 39 | +<html> |
| 40 | +<head><title>editcount</title> |
| 41 | +</head> |
| 42 | +<body>""" |
| 43 | + |
| 44 | +namespaces = { |
| 45 | + 0: 'Articles', |
| 46 | + 1: 'Talk', |
| 47 | + 2: 'User', |
| 48 | + 3: 'User talk', |
| 49 | + 4: 'Wikipedia', |
| 50 | + 5: 'Wikipedia talk', |
| 51 | + 6: 'Image', |
| 52 | + 7: 'Image talk', |
| 53 | + 8: 'MediaWiki', |
| 54 | + 9: 'MediaWiki talk', |
| 55 | + 10: 'Template', |
| 56 | + 11: 'Template talk', |
| 57 | + 12: 'Help', |
| 58 | + 13: 'Help talk', |
| 59 | + 14: 'Category', |
| 60 | + 15: 'Category talk' |
| 61 | +} |
| 62 | + |
| 63 | +def ns2name(ns): |
| 64 | + return namespaces[ns] |
| 65 | + |
| 66 | +def editcount(user, nsb): |
| 67 | + ns = dict() |
| 68 | + total = 0 |
| 69 | + db = MySQLdb.connect(db=dbname, host=dbserver, user=dbuser, passwd=dbpassword) |
| 70 | + c = db.cursor() |
| 71 | + c.execute("SELECT user_id FROM user WHERE user_name=%s", user) |
| 72 | + t = c.fetchone() |
| 73 | + if t == None: |
| 74 | + print "<strong>user '" + cgi.escape(user) + "' does not exist</strong><br/>" |
| 75 | + return |
| 76 | + uid = t[0] |
| 77 | + if nsb: |
| 78 | + for i in range(0, 16): |
| 79 | + c.execute("SELECT COUNT(*) FROM cur WHERE cur_user=%s AND cur_namespace=%s", (uid, i)) |
| 80 | + t = c.fetchone() |
| 81 | + ns[i] = t[0] |
| 82 | + c.execute("SELECT COUNT(*) FROM old WHERE old_user=%s AND old_namespace=%s", (uid, i)) |
| 83 | + t = c.fetchone() |
| 84 | + ns[i] += t[0] |
| 85 | + print """ |
| 86 | +<table cellspacing="0"> |
| 87 | +<tr><th style="border-bottom: solid 1px black; border-right: solid 1px black">namespace</th><th style="border-bottom: solid 1px black">edits</th></tr> |
| 88 | +""" |
| 89 | + for nsn in ns.keys(): |
| 90 | + print "<tr><td style='border-right: solid 1px black'>%s</td><td style='text-align: right'>%d</td></tr>" % (ns2name(nsn), ns[nsn]) |
| 91 | + total += ns[nsn] |
| 92 | + print "</table>" |
| 93 | + else: |
| 94 | + c.execute("SELECT COUNT(*) FROM cur WHERE cur_user=%s", uid) |
| 95 | + t = c.fetchone() |
| 96 | + total = int(t[0]) |
| 97 | + c.execute("SELECT COUNT(*) FROM old WHERE old_user=%s", uid) |
| 98 | + t = c.fetchone() |
| 99 | + total += t[0] |
| 100 | + print cgi.escape(user) + " has a total of %d edits<br/>" % total |
| 101 | + return |
| 102 | + |
| 103 | +if f.has_key('user'): |
| 104 | + print "<div>" |
| 105 | + print "<br/>" |
| 106 | + nsb = False |
| 107 | + # too slow |
| 108 | + #if f.has_key('nsb'): |
| 109 | + # nsb = True |
| 110 | + editcount(f['user'].value, nsb) |
| 111 | + print "</div>" |
| 112 | + |
| 113 | +print """ |
| 114 | +<hr/> |
| 115 | +<form action="count_edits" method="get"> |
| 116 | +user name: <input type="text" name="user"/> |
| 117 | +<select name="dbname"> |
| 118 | +""" |
| 119 | + |
| 120 | +dblist = getdblist() |
| 121 | +dblist.sort() |
| 122 | +for db in dblist: |
| 123 | + selected = '' |
| 124 | + if db == dbname: |
| 125 | + selected=' selected="selected"' |
| 126 | + print '<option value="%s"%s>%s</option>' % (db, selected,db) |
| 127 | +print """ |
| 128 | +</select> |
| 129 | +<input type="submit" value="go" /> |
| 130 | +<!--<input type="checkbox" name="nsb"/>breakdown by namespace (slow)--> |
| 131 | +<br/> |
| 132 | +<p> |
| 133 | +<strong>warning:</strong> <em>editcountitis can be fatal</em> |
| 134 | +</p> |
| 135 | +<hr/> |
| 136 | +kate's tools: |
| 137 | +<strong>user edit counter</strong> |
| 138 | +| <a href="six_degrees">six degrees of wikipedia</a> |
| 139 | +</form> |
| 140 | +</body> |
| 141 | +</html> |
| 142 | +""" |
Property changes on: branches/mark/udpmcast/edit_counter/count_edits.py |
___________________________________________________________________ |
Added: svn:keywords |
1 | 143 | + Author Date Id Revision |
Added: svn:eol-style |
2 | 144 | + native |
Added: svn:executable |
3 | 145 | + * |