001package votorola.s.line; // Copyright 2009-2011, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. 002 003import java.io.*; 004import java.net.*; 005import java.util.*; 006import java.util.regex.*; 007import javax.script.*; 008import votorola.a.*; 009import votorola.a.response.line.*; 010import votorola.g.*; 011import votorola.g.io.*; 012import votorola.g.lang.*; 013import votorola.g.logging.*; 014import votorola.g.net.*; 015import votorola.g.option.*; 016 017 018/** Main class of the executable <code>vomir</code> - a slapdash prototype of vote 019 * mirroring that worked against White House 2 servers. It ran until early 2011, when 020 * those servers were shut down. The <a 021 * href='http://reluk.ca/system/host/obsidian/home/v/votorola/in/vomir/' target='_top'>raw 022 * images</a> have since been frozen in place and are still being counted, as you can see 023 * in the results of poll <a 024 * href='http://reluk.ca:8080/v/w/Votespace?u=Mike-ZeleaCom&p=G!p!edor' 025 * target='_top'>G/p/edor</a>. Red numeral 1's mark the votes that were mirrored. 026 * 027 * @see <a href='../../../../../s/manual.xht#line-vomir' target='_top'>vomir</a> 028 */ 029public @Warning("dead code") final class VOMir 030{ 031 032 033 /** Executes from the command line. 034 * 035 * @param argv command line argument array 036 */ 037 public static @ThreadSafe void main( String[] argv ) 038 { 039 LoggerX.i(VOMir.class).info( "vomir is running with arguments " + Arrays.toString( argv )); 040 final Map<String,Option> optionMap = CommandLine.compileBaseOptions(); 041 final int aFirstNonOption = GetoptX.parse( "vomir", argv, optionMap ); 042 if( aFirstNonOption < argv.length ) 043 { 044 System.err.println( GetoptX.createUnexpectedArgWarning( 045 "vomir", argv[aFirstNonOption] )); 046 System.err.println( GetoptX.createHelpPrompt( "vomir" )); 047 System.exit( 1 ); 048 } 049 050 if( optionMap.get("help").hasOccured() ) 051 { 052 System.out.print 053 ( 054 "Usage: vomir\n" + 055 "Pull remote votes for local mirroring.\n" 056 ); 057 System.exit( 0 ); 058 } 059 060 try 061 { 062 new VOMir().run(); 063 } 064 catch( RuntimeException x ) { throw x; } 065 catch( Exception x ) 066 { 067 System.err.print( "voter: fatal error" + Votorola.unmessagedDetails(x) + ": " ); 068 x.printStackTrace( System.err ); 069 System.exit( 1 ); 070 } 071 } 072 073 074 075 private VOMir() throws IOException, javax.script.ScriptException, java.sql.SQLException, 076 URISyntaxException {} 077 078 079 080 // ------------------------------------------------------------------------------------ 081 082 083 /** The pattern of a precedence section marker in White House 2. The votes are 084 * sectioned by precedence. 085 */ 086 private static @ThreadSafe final Pattern NAT_PRECEDENCE_PATTERN = Pattern.compile( 087 ">Priority #(\\d+)<" ); 088 089 090 091 /** The pattern of a timestamp in White House 2. 092 */ 093 private static @ThreadSafe final Pattern NAT_TIME_PATTERN = Pattern.compile( 094 "\"(\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d.+)\"" ); 095 // 2 0 0 9 - 1 2 - 2 5 T 1 0 : 2 8 : 2 7 Z 096 097 098 /** The pattern of a user-page link in White House 2. 099 */ 100 private static @ThreadSafe final Pattern NAT_USER_LINK_PATTERN = Pattern.compile( 101 "<a href=\"/users/(\\d+)-" ); 102 103 104 105 /** Returns the next line that is neither empty, nor all whitespace. 106 */ 107 private static @ThreadSafe String nextSolidLine( final BufferedReader in ) throws IOException 108 { 109 for( ;; ) 110 { 111 final String s = in.readLine(); 112 if( s == null ) return s; 113 114 if( s.length() == 0 ) continue; 115 116 for( int c = s.length() - 1; c >= 0; --c ) 117 { 118 if( !Character.isWhitespace( s.charAt( c ))) return s; 119 } 120 } 121 } 122 123 124 125 // /** Constructs a new stream reader that is configured to read documents that are 126 // * un-namespaced. 127 // */ 128 // private static @ThreadSafe XMLStreamReader newXMLStreamReader( final Reader reader ) 129 // throws XMLStreamException 130 // { 131 // return VoterInputTable.newXMLStreamReader( reader ); 132 // } 133 134 135 136 private final VoteServer.Run vsRun; 137 { 138 vsRun = new VoteServer( System.getProperty( "user.name" )).new Run( 139 /*isSingleThreaded*/true ); 140 vsRun.singleServiceLock().lock(); // no need to unlock, single access 141 } 142 143 144 145 /** Attempts to pull from a White House 2 vote-server. Creates a new source snap 146 * directory in the process, and sets it as the current one. If the attempt fails, 147 * the previous snap directory is left current. 148 */ 149 private void pullWH1_server( final String siteLoc, final String sourceMnemonic, 150 final HashMap<String,String> pollMap, final HashMap<String,MailOb> uidMap ) 151 { 152 final File sourceDirectory = new File( vsRun.voteServer().inDirectory(), 153 "vomir" + File.separator + sourceMnemonic ); 154 try 155 { 156 final File snapDirectory = OutputStore.mkdirsY4MDS( sourceDirectory, "snap" ); 157 final String pollName = "G/p/edor"; 158 final String priorityS = pollMap.get( pollName ); 159 // To allow multiple priorities (proposals) per poll in future, prune 160 // duplicate votes based on the voter's ranking of the priority (vote 161 // precedence). 162 if( priorityS == null ) LoggerX.i(getClass()).config( "no '" + sourceMnemonic + "' priority page mapped to poll: " + pollName ); 163 164 final File file = new File( snapDirectory, pollName + ".xml" ); 165 FileX.ensuredirs( file.getParentFile() ); 166 boolean isFileCleanlyTerminated = false; 167 final BufferedWriter o = new BufferedWriter( new OutputStreamWriter( 168 new FileOutputStream( file ), "UTF-8" )); 169 try 170 { 171 o.append( "<?xml version='1.0' encoding='UTF-8'?> <!-- -*-coding: utf-8;-*- -->\n" 172 + "<in serviceName='" + pollName + "'>\n" ); 173 pullWH2_poll( siteLoc, uidMap, priorityS, /*toPullEndorsers*/true, 174 sourceDirectory, o ); 175 pullWH2_poll( siteLoc, uidMap, priorityS, /*toPullEndorsers*/false, 176 sourceDirectory, o ); 177 o.append( " </in>\n" ); 178 isFileCleanlyTerminated = true; 179 } 180 finally 181 { 182 o.close(); 183 if( !isFileCleanlyTerminated ) file.delete(); 184 } 185 186 final File snapLink = new File( sourceDirectory, "_snap_current" ); 187 FileX.symlink( snapDirectory.getName(), snapLink.getPath() ); 188 } 189 catch( IOException x ) { LoggerX.i(getClass()).log( LoggerX.INFO, /*message*/"unable to complete pull from site " + siteLoc + ":" + Votorola.unmessagedDetails(x), x ); } 190 } 191 192 193 194 /** Pulls from a White House 2 poll (priority). 195 */ 196 private void pullWH2_poll( final String siteLoc, HashMap<String,MailOb> uidMap, 197 final String priorityS, final boolean toPullEndorsers, final File sourceDirectory, 198 final BufferedWriter o ) throws IOException 199 { 200 final String priorityLoc = siteLoc + "/priorities/" + priorityS; 201 final String sourceMnemonic = sourceDirectory.getName(); 202 final String candidateMailLoc = (toPullEndorsers? "x-":"o-") + sourceMnemonic 203 + "-" + priorityS; 204 final String docLoc = toPullEndorsers? priorityLoc: priorityLoc + "/opposers"; 205 o.append( " <pos>\n" 206 + " <subj dom='zelea.com' loc='" + candidateMailLoc + "'/>\n" 207 + " <doc url='" + docLoc + "'/>\n" 208 + " </pos>\n" ); 209 210 final HttpURLConnection http; 211 { 212 final URL url = new URL( priorityLoc + "/" 213 + (toPullEndorsers? "endorsers":"opposers") ); 214 LoggerX.i(getClass()).fine( "requesting page " + url ); 215 http = (HttpURLConnection)( url.openConnection() ); 216 } 217 URLConnectionX.connect( http ); 218 try 219 { 220 final BufferedReader in = new BufferedReader( new InputStreamReader( 221 http.getInputStream(), "UTF-8" )); 222 try 223 { 224 // final XMLStreamReader r = newXMLStreamReader( /*systemId*/priorityLoc, in ); 225 //// not well formed, so parse it by pattern matching: 226 Matcher m = null; 227 String precedence = "1"; // till proven otherwise 228 for( ;; ) 229 { 230 String s = nextSolidLine( in ); 231 if( s == null ) break; 232 233 m = NAT_PRECEDENCE_PATTERN.matcher( s ); 234 if( m.find() ) 235 { 236 precedence = m.group( 1 ); 237 continue; 238 } 239 240 m = NAT_USER_LINK_PATTERN.matcher( s ); 241 if( m.find() ) 242 { 243 s = nextSolidLine( in ); 244 if( s == null ) break; 245 246 final String userNumberS = m.group( 1 ); 247 final MailOb voterMailOb = uidMap.get( userNumberS ); 248 if( voterMailOb == null ) continue; // unregistered 249 250 m = NAT_TIME_PATTERN.matcher( s ); 251 if( m.find() ) 252 { 253 final String timeS = m.group( 1 ); 254 o.append( " <pos>\n" 255 + " <subj dom='" + voterMailOb.dom + "' loc='" + voterMailOb.loc + "'/>\n" 256 + " <doc url='" + docLoc + "'/>\n" 257 + " <vote t='" + timeS + "'>\n" 258 + " <obj dom='zelea.com' loc='" + candidateMailLoc + "'/>\n" 259 + " <prec>" + precedence + "</prec>\n" 260 + " </vote>\n" 261 + " </pos>\n" ); 262 } 263 } 264 } 265 } 266 finally{ in.close(); } 267 } 268 finally{ http.disconnect(); } 269 } 270 271 272 273 private void run() 274 { 275 pullWH1_server( "http://www.whitehouse2.org", "natW", pollMap_natW, uidMap_natW ); 276 pullWH1_server( "http://www.parliament2.ca", "natP", pollMap_natP, uidMap_natP ); 277 pullWH1_server( "http://au.nationbuilder.com", "natA", pollMap_natA, uidMap_natA ); 278 } 279 280 281 282 // ` N a t - A ```````````````````````````````````````````````````````````````````````` 283 284 285 /** Map of priority number-strings on the Australia2 server, keyed by poll name. 286 */ 287 private final HashMap<String,String> pollMap_natA; 288 { 289 final HashMap<String,String> m = new HashMap<String,String>(); 290 pollMap_natA = m; 291 m.put( "G/p/edor", "72" ); 292 } 293 294 295 296 /** Map of email addresses keyed by user number-strings on the Australia2 server. 297 */ 298 private final HashMap<String,MailOb> uidMap_natA; 299 { 300 final HashMap<String,MailOb> m = new HashMap<String,MailOb>(); 301 uidMap_natA = m; 302 m.put( "136", new MailOb ( "mike", "zelea.com" )); 303 } 304 305 306 307 // ` N a t - P ```````````````````````````````````````````````````````````````````````` 308 309 310 /** Map of priority number-strings on the Parliament2 server, keyed by poll name. 311 */ 312 private final HashMap<String,String> pollMap_natP; 313 { 314 final HashMap<String,String> m = new HashMap<String,String>(); 315 pollMap_natP = m; 316 m.put( "G/p/edor", "99" ); 317 } 318 319 320 321 /** Map of email addresses keyed by user number-strings on the Parliament2 server. 322 */ 323 private final HashMap<String,MailOb> uidMap_natP; 324 { 325 final HashMap<String,MailOb> m = new HashMap<String,MailOb>(); 326 uidMap_natP = m; 327 m.put( "1", new MailOb ( "trevor", "canadaka.net" )); 328 m.put( "563", new MailOb ( "mike", "zelea.com" )); 329 m.put( "564", new MailOb ( "ThomasvonderElbe", "gmx.de" )); 330 } 331 332 333 334 // ` N a t - W ```````````````````````````````````````````````````````````````````````` 335 336 337 /** Map of priority number-strings on the Whitehouse2 server, keyed by poll name. 338 */ 339 private final HashMap<String,String> pollMap_natW; 340 { 341 final HashMap<String,String> m = new HashMap<String,String>(); 342 pollMap_natW = m; 343 m.put( "G/p/edor", "2365" ); 344 } 345 346 347 348 /** Map of email addresses keyed by user number-strings on the Whitehouse2 server. 349 */ 350 private final HashMap<String,MailOb> uidMap_natW; 351 { 352 final HashMap<String,MailOb> m = new HashMap<String,MailOb>(); 353 uidMap_natW = m; 354 m.put( "1", new MailOb ( "jim", "gilliam.com" )); // though gilliam.com is listed as for sale, he uses this address in lists etc. 355 m.put( "10333", new MailOb ( "mike", "zelea.com" )); 356 m.put( "10346", new MailOb ( "ThomasvonderElbe", "gmx.de" )); 357 } 358 359 360 361 // ==================================================================================== 362 363 364 private static final class MailOb 365 { 366 367 MailOb( final String loc, final String dom ) 368 { 369 this.loc = loc; 370 this.dom = dom; 371 } 372 373 374 private final String dom; 375 376 377 private final String loc; 378 379 } 380 381 382}