github.com/razvanm/vanadium-go-1.3@v0.0.0-20160721203343-4a65068e5915/src/runtime/ppapi/syscall_fd_nacl.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ppapi 6 7 import ( 8 "encoding/base64" 9 "fmt" 10 "math/rand" 11 "runtime" 12 "strconv" 13 "strings" 14 "sync" 15 "syscall" 16 ) 17 18 // files is the table indexed by file descriptor. 19 var files struct { 20 sync.RWMutex 21 tab []*file 22 } 23 24 // A file is an open file, something with a file descriptor. 25 // A particular *file may appear in the files table multiple times, due to use of Dup or Dup2. 26 type file struct { 27 fdref int // used in files.tab 28 impl fileImpl // underlying implementation 29 } 30 31 // A fileImpl is the implementation of something that can be a file. 32 type fileImpl interface { 33 // Standard operations. 34 // These can be called concurrently from multiple goroutines. 35 stat(*syscall.Stat_t) error 36 read([]byte) (int, error) 37 write([]byte) (int, error) 38 seek(int64, int) (int64, error) 39 pread([]byte, int64) (int, error) 40 pwrite([]byte, int64) (int, error) 41 42 // Close is called when the last reference to a *file is removed 43 // from the file descriptor table. It may be called concurrently 44 // with active operations such as blocked read or write calls. 45 close() error 46 } 47 48 // newFD adds impl to the file descriptor table, 49 // returning the new file descriptor. 50 // Like Unix, it uses the lowest available descriptor. 51 func newFD(impl fileImpl) int { 52 files.Lock() 53 defer files.Unlock() 54 f := &file{impl: impl, fdref: 1} 55 for fd, oldf := range files.tab { 56 // Look if there is a space for a new file 57 if oldf == nil { 58 files.tab[fd] = f 59 return fd 60 } 61 } 62 fd := len(files.tab) 63 files.tab = append(files.tab, f) 64 return fd 65 } 66 67 var stdin = &defaultFileImpl{} 68 var stdout = &consoleLogFile{logLevel: PP_LOGLEVEL_LOG} 69 var stderr = &consoleLogFile{logLevel: PP_LOGLEVEL_WARNING} 70 71 // Install Native Client stdin, stdout, stderr. 72 func init_fds() { 73 if len(files.tab) != 0 { 74 return 75 } 76 newFD(stdin) 77 newFD(stdout) 78 newFD(stderr) 79 } 80 81 func fdToFileRep(fd int) (*file, error) { 82 files.Lock() 83 defer files.Unlock() 84 if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil { 85 return nil, syscall.EBADF 86 } 87 return files.tab[fd], nil 88 } 89 90 // fdToFile retrieves the *file corresponding to a file descriptor. 91 func fdToFile(fd int) (fileImpl, error) { 92 file, err := fdToFileRep(fd) 93 if err != nil { 94 return nil, err 95 } 96 return file.impl, nil 97 } 98 99 var symLinks map[string]string 100 var tmpFiles map[string]*bytesBufFileData 101 102 func initSymlinks() { 103 if symLinks == nil { 104 symLinks = map[string]string{} 105 } 106 } 107 108 func initTmpFiles() { 109 if tmpFiles == nil { 110 tmpFiles = map[string]*bytesBufFileData{} 111 } 112 } 113 114 func resolveSymlink(path string) string { 115 initSymlinks() 116 seen := map[string]bool{} 117 for { 118 if _, ok := seen[path]; ok { 119 panic(fmt.Sprintf("Infinite loop in symlink %s %v", path, seen)) 120 } 121 seen[path] = true 122 if nextPath, ok := symLinks[path]; ok { 123 path = nextPath 124 } else { 125 break 126 } 127 } 128 return path 129 } 130 131 func (PPAPISyscallImpl) Open(path string, mode int, perm uint32) (fd int, err error) { 132 path = resolveSymlink(path) 133 //fmt.Printf("Opening: \"%s\" (resolved to \"%s\") %d %d\n", origPath, path, mode, perm) 134 // TODO(bprosnitz) Handle modes better 135 initEnvVars() 136 if strings.HasPrefix(path, envVars["TMPDIR"]) { 137 initTmpFiles() 138 data, ok := tmpFiles[path] 139 if !ok { 140 data = newByteBufFileData(path) 141 tmpFiles[path] = data 142 } 143 bbFile := &bytesBufFile{ 144 dat: data, 145 index: 0, 146 } 147 return newFD(bbFile), nil 148 } 149 if strings.HasPrefix(path, "/usr/local/google/home") { 150 return 0, fmt.Errorf("File not found: %s", path) 151 } 152 switch path { 153 case "/etc/localtime": 154 data, err := base64.StdEncoding.DecodeString(base64_localtime) 155 if err != nil { 156 panic(fmt.Sprintf("Error decoding base64: %v", err)) 157 } 158 return newFD(&bytesReadFile{data, 0, sync.Mutex{}}), nil 159 case "/dev/urandom": 160 return newFD(&randomImpl{}), nil 161 default: 162 panic(fmt.Sprintf("Open() not implemented. Path: %s", path)) 163 } 164 } 165 166 func (PPAPISyscallImpl) Close(fd int) error { 167 files.Lock() 168 if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil { 169 files.Unlock() 170 return syscall.EBADF 171 } 172 f := files.tab[fd] 173 files.tab[fd] = nil 174 f.fdref-- 175 fdref := f.fdref 176 if fdref > 0 { 177 files.Unlock() 178 panic("Shouldn't get here until Dup/Dup2 is implemented") 179 return nil 180 } 181 files.Unlock() 182 return f.impl.close() 183 } 184 185 func (PPAPISyscallImpl) CloseOnExec(fd int) { 186 // nothing to do - no exec 187 } 188 189 func (PPAPISyscallImpl) Dup(fd int) (int, error) { 190 panic("Dup not yet uncommented") 191 /* 192 files.Lock() 193 defer files.Unlock() 194 if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil { 195 return -1, EBADF 196 } 197 f := files.tab[fd] 198 f.fdref++ 199 for newfd, oldf := range files.tab { 200 if oldf == nil { 201 files.tab[newfd] = f 202 return newfd, nil 203 } 204 } 205 newfd := len(files.tab) 206 files.tab = append(files.tab, f) 207 return newfd, nil*/ 208 } 209 210 func (PPAPISyscallImpl) Dup2(fd, newfd int) error { 211 panic("Dup2 not yet uncommented") 212 /* 213 files.Lock() 214 defer files.Unlock() 215 if fd < 0 || fd >= len(files.tab) || files.tab[fd] == nil || newfd < 0 || newfd >= len(files.tab)+100 { 216 files.Unlock() 217 return EBADF 218 } 219 f := files.tab[fd] 220 f.fdref++ 221 for cap(files.tab) <= newfd { 222 files.tab = append(files.tab[:cap(files.tab)], nil) 223 } 224 oldf := files.tab[newfd] 225 var oldfdref int 226 if oldf != nil { 227 oldf.fdref-- 228 oldfdref = oldf.fdref 229 } 230 files.tab[newfd] = f 231 files.Unlock() 232 if oldf != nil { 233 if oldfdref == 0 { 234 oldf.impl.close() 235 } 236 } 237 return nil*/ 238 } 239 240 var dirMade map[string]bool 241 242 func (PPAPISyscallImpl) Stat(path string, stat *syscall.Stat_t) (err error) { 243 switch path { 244 case "/tmp/revocation_dir/caveat_dir", "/tmp/revocation_dir", "/tmp/revocation_dir/revocation_dir": 245 if dirMade != nil { 246 if _, ok := dirMade[path]; ok { 247 stat.Mode = syscall.S_IFDIR 248 return nil 249 } 250 } 251 return fmt.Errorf("path %s does not exist", path) 252 case "/tmp": 253 stat.Mode = syscall.S_IFDIR 254 return nil 255 default: 256 panic(fmt.Sprintf("Stat() not implemented for %s", path)) 257 } 258 } 259 260 func (PPAPISyscallImpl) Mkdir(path string, mode uint32) (err error) { 261 switch path { 262 case "/tmp/revocation_dir/caveat_dir", "/tmp/revocation_dir/revocation_dir", "/tmp/revocation_dir": 263 if dirMade == nil { 264 dirMade = map[string]bool{} 265 } 266 dirMade[path] = true 267 return nil 268 269 default: 270 panic(fmt.Sprintf("Mkdir() not implemented for %s", path)) 271 } 272 } 273 274 func (PPAPISyscallImpl) Fstat(fd int, st *syscall.Stat_t) error { 275 panic("Fstat not yet uncommented") 276 /* 277 f, err := fdToFile(fd) 278 if err != nil { 279 return err 280 } 281 return f.impl.stat(st)*/ 282 } 283 284 func (PPAPISyscallImpl) Read(fd int, b []byte) (int, error) { 285 f, err := fdToFile(fd) 286 if err != nil { 287 return 0, err 288 } 289 return f.read(b) 290 } 291 292 func (PPAPISyscallImpl) Write(fd int, b []byte) (int, error) { 293 f, err := fdToFile(fd) 294 if err != nil { 295 return 0, err 296 } 297 return f.write(b) 298 } 299 300 func (PPAPISyscallImpl) Pread(fd int, b []byte, offset int64) (int, error) { 301 panic("Pread not yet uncommented") 302 /* 303 f, err := fdToFile(fd) 304 if err != nil { 305 return 0, err 306 } 307 return f.impl.pread(b, offset)*/ 308 } 309 310 func (PPAPISyscallImpl) Pwrite(fd int, b []byte, offset int64) (int, error) { 311 panic("Pwrite not yet uncommented") 312 /* 313 f, err := fdToFile(fd) 314 if err != nil { 315 return 0, err 316 } 317 return f.impl.pwrite(b, offset)*/ 318 } 319 320 func (PPAPISyscallImpl) Seek(fd int, offset int64, whence int) (int64, error) { 321 panic("Seek not yet uncommented") 322 /*f, err := fdToFile(fd) 323 if err != nil { 324 return 0, err 325 } 326 return f.impl.seek(offset, whence)*/ 327 } 328 329 func (PPAPISyscallImpl) Unlink(path string) (err error) { 330 //fmt.Printf("Unlinking: %s Links: %v\n", path, symLinks) 331 initSymlinks() 332 if _, ok := symLinks[path]; ok { 333 delete(symLinks, path) 334 return nil 335 } 336 return fmt.Errorf("File %s not a symlink", path) 337 } 338 339 func (PPAPISyscallImpl) Rmdir(path string) (err error) { 340 //fmt.Printf("Attempting to remove dir: %s\n", path) 341 path = resolveSymlink(path) 342 switch path { 343 case "/tmp/NaClMain.FATAL", "/tmp/NaClMain.ERROR", "/tmp/NaClMain.WARNING", "/tmp/NaClMain.INFO": 344 return fmt.Errorf("Dir %s not found", path) 345 default: 346 panic(fmt.Sprintf("Rmdir() %s not implemented]", path)) 347 } 348 } 349 350 func (PPAPISyscallImpl) Symlink(oldpath string, newpath string) (err error) { 351 //fmt.Printf("Symlinked %s to %s\n", newpath, oldpath) 352 initSymlinks() 353 symLinks[newpath] = oldpath 354 return nil 355 } 356 357 func (PPAPISyscallImpl) Fsync(fd int) (err error) { 358 // This is a no-op because everything is in memory right now. 359 // Implement if this changes. 360 return err 361 } 362 363 type consoleLogFile struct { 364 impl PPAPISyscallImpl 365 logLevel LogLevel 366 } 367 368 func (*consoleLogFile) close() error { return nil } 369 func (*consoleLogFile) stat(*syscall.Stat_t) error { 370 panic("stat not implemented") 371 } 372 func (*consoleLogFile) read([]byte) (int, error) { 373 panic("Cannot read from log file") 374 } 375 func (c *consoleLogFile) writeLine(b []byte) (int, error) { 376 _, file, line, _ := runtime.Caller(3) 377 loc := file + ":" + strconv.Itoa(line) 378 // Unfortunately nacl truncates logs at 128 chars. 379 batchSize := 128 380 for i := 0; i*batchSize < len(b); i++ { 381 min := i * batchSize 382 max := (i + 1) * batchSize 383 if max > len(b) { 384 max = len(b) 385 } 386 c.impl.Instance.LogWithSourceString(c.logLevel, loc, string(b[min:max])) 387 } 388 return len(b), nil 389 } 390 func (c *consoleLogFile) write(b []byte) (int, error) { 391 s := string(b) 392 parts := strings.Split(s, "\n") 393 written := len(parts) - 1 // newlines 394 for _, part := range parts { 395 additionalWrite, err := c.writeLine([]byte(part)) 396 if err != nil { 397 return 0, err 398 } 399 written += additionalWrite 400 } 401 return written, nil 402 } 403 func (*consoleLogFile) seek(int64, int) (int64, error) { 404 panic("Cannot seek in log file.") 405 } 406 func (c *consoleLogFile) pread(b []byte, offset int64) (int, error) { 407 return c.read(b[offset:]) 408 } 409 func (c *consoleLogFile) pwrite(b []byte, offset int64) (int, error) { 410 return c.write(b[offset:]) 411 } 412 413 // defaulFileImpl imlements fileImpl. 414 // It can be embedded to complete a partial fileImpl implementation. 415 type defaultFileImpl struct{} 416 417 func (*defaultFileImpl) close() error { return nil } 418 func (*defaultFileImpl) stat(*syscall.Stat_t) error { return syscall.ENOSYS } 419 func (*defaultFileImpl) read([]byte) (int, error) { return 0, syscall.ENOSYS } 420 func (*defaultFileImpl) write([]byte) (int, error) { return 0, syscall.ENOSYS } 421 func (*defaultFileImpl) seek(int64, int) (int64, error) { return 0, syscall.ENOSYS } 422 func (*defaultFileImpl) pread([]byte, int64) (int, error) { return 0, syscall.ENOSYS } 423 func (*defaultFileImpl) pwrite([]byte, int64) (int, error) { return 0, syscall.ENOSYS } 424 425 type randomImpl struct{} 426 427 func (*randomImpl) close() error { return nil } 428 func (*randomImpl) stat(*syscall.Stat_t) error { return syscall.ENOSYS } 429 func (*randomImpl) read(b []byte) (int, error) { 430 // TODO(bprosnitz) Make cryptographically secure? 431 for i, _ := range b { 432 b[i] = byte(rand.Uint32()) 433 } 434 return len(b), nil 435 } 436 func (*randomImpl) write([]byte) (int, error) { return 0, syscall.ENOSYS } 437 func (*randomImpl) seek(int64, int) (int64, error) { return 0, syscall.ENOSYS } 438 func (r *randomImpl) pread(b []byte, offset int64) (int, error) { 439 return r.read(b[offset:]) 440 } 441 func (*randomImpl) pwrite([]byte, int64) (int, error) { return 0, syscall.ENOSYS } 442 443 type bytesReadFile struct { 444 bytes []byte 445 indexRead int 446 lock sync.Mutex 447 } 448 449 func (*bytesReadFile) close() error { return nil } 450 func (*bytesReadFile) stat(*syscall.Stat_t) error { 451 panic("Stat not implemented") 452 } 453 func (brf *bytesReadFile) read(b []byte) (int, error) { 454 brf.lock.Lock() 455 amt := copy(b, brf.bytes[brf.indexRead:]) 456 brf.indexRead += amt 457 brf.lock.Unlock() 458 return amt, nil 459 } 460 func (*bytesReadFile) write([]byte) (int, error) { 461 panic("Cannot write to a read file") 462 } 463 func (*bytesReadFile) seek(int64, int) (int64, error) { 464 panic("Seek not implemented") 465 } 466 func (brf *bytesReadFile) pread(b []byte, offset int64) (int, error) { 467 return brf.read(b[offset:]) 468 } 469 func (*bytesReadFile) pwrite([]byte, int64) (int, error) { 470 panic("Cannot write to a read file") 471 } 472 473 type bytesBufFileData struct { 474 bytes []byte 475 lock sync.Mutex 476 path string // This is just for debugging, can be removed. 477 } 478 479 type bytesBufFile struct { 480 dat *bytesBufFileData 481 index int 482 } 483 484 func newByteBufFileData(path string) *bytesBufFileData { 485 return &bytesBufFileData{ 486 bytes: []byte{}, 487 path: path, 488 } 489 } 490 func (bbf *bytesBufFile) close() error { 491 bbf.dat = nil 492 return nil 493 } 494 func (*bytesBufFile) stat(*syscall.Stat_t) error { 495 panic("Stat not implemented") 496 } 497 func (bbf *bytesBufFile) read(b []byte) (int, error) { 498 if bbf.dat == nil { 499 panic("Cannot read closed file") 500 } 501 bbf.dat.lock.Lock() 502 amt := copy(b, bbf.dat.bytes[bbf.index:]) 503 bbf.index += amt 504 bbf.dat.lock.Unlock() 505 return amt, nil 506 } 507 func (bbf *bytesBufFile) write(b []byte) (int, error) { 508 if bbf.dat == nil { 509 panic("Cannot write to closed file") 510 } 511 bbf.dat.lock.Lock() 512 neededSize := bbf.index + len(b) + 1 513 if neededSize >= len(bbf.dat.bytes) { 514 newBuf := make([]byte, neededSize) 515 copy(newBuf, bbf.dat.bytes) 516 bbf.dat.bytes = newBuf 517 } 518 fmt.Printf("temp file %s is now %d bytes long", bbf.dat.path, len(bbf.dat.bytes)) 519 if copy(bbf.dat.bytes[bbf.index:], b) != len(b) { 520 panic("Invalid copy during write") 521 } 522 bbf.index += len(b) 523 bbf.dat.lock.Unlock() 524 return len(b), nil 525 } 526 func (*bytesBufFile) seek(int64, int) (int64, error) { 527 panic("Seek not implemented") 528 } 529 func (bbf *bytesBufFile) pread(b []byte, offset int64) (int, error) { 530 return bbf.read(b[offset:]) 531 } 532 func (bbf *bytesBufFile) pwrite(b []byte, offset int64) (int, error) { 533 return bbf.write(b[offset:]) 534 } 535 536 const base64_localtime = "VFppZjIAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAAAAAC5AAAABAAAABCepkig" + 537 "n7sVkKCGKqChmveQy4kaoNIj9HDSYSYQ1v50INiArZDa/tGg28CQENzes6DdqayQ" + 538 "3r6VoN+JjpDgnneg4WlwkOJ+WaDjSVKQ5F47oOUpNJDmR1gg5xJREOgnOiDo8jMQ" + 539 "6gccIOrSFRDr5v4g7LH3EO3G4CDukdkQ76/8oPBxuxDxj96g8n/BkPNvwKD0X6OQ" + 540 "9U+ioPY/hZD3L4Sg+CiiEPkPZqD6CIQQ+viDIPvoZhD82GUg/chIEP64RyD/qCoQ" + 541 "AJgpIAGIDBACeAsgA3EokARhJ6AFUQqQBkEJoAcw7JAHjUOgCRDOkAmtvyAK8LCQ" + 542 "C+CvoAzZzRANwJGgDrmvEA+priAQmZEQEYmQIBJ5cxATaXIgFFlVEBVJVCAWOTcQ" + 543 "Fyk2IBgiU5AZCRggGgI1kBryNKAb4heQHNIWoB3B+ZAesfigH6HbkCB2KyAhgb2Q" + 544 "IlYNICNq2hAkNe8gJUq8ECYV0SAnKp4QJ/7toCkKgBAp3s+gKupiECu+saAs036Q" + 545 "LZ6ToC6zYJAvfnWgMJNCkDFnkiAycySQM0d0IDRTBpA1J1YgNjLokDcHOCA4HAUQ" + 546 "OOcaIDn75xA6xvwgO9vJEDywGKA9u6sQPo/6oD+bjRBAb9ygQYSpkEJPvqBDZIuQ" + 547 "RC+goEVEbZBF89MgRy2KEEfTtSBJDWwQSbOXIErtThBLnLOgTNZqkE18laBOtkyQ" + 548 "T1x3oFCWLpBRPFmgUnYQkFMcO6BUVfKQVPwdoFY11JBW5TogWB7xEFjFHCBZ/tMQ" + 549 "WqT+IFvetRBchOAgXb6XEF5kwiBfnnkQYE3eoGGHlZBiLcCgY2d3kGQNoqBlR1mQ" + 550 "Ze2EoGcnO5BnzWagaQcdkGmtSKBq5v+Qa5ZlIGzQHBBtdkcgbq/+EG9WKSBwj+AQ" + 551 "cTYLIHJvwhBzFe0gdE+kEHT/CaB2OMCQdt7roHgYopB4vs2gefiEkHqer6B72GaQ" + 552 "fH6RoH24SJB+XnOgf5gqkAABAAECAwEAAQABAAEAAQABAAEAAQABAAEAAQABAAEA" + 553 "AQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEA" + 554 "AQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEA" + 555 "AQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEAAQABAAEA" + 556 "AQABAAEAAQAB//+dkAEA//+PgAAE//+dkAEI//+dkAEMUERUAFBTVABQV1QAUFBU" + 557 "AAAAAAEAAAABVFppZjIAAAAAAAAAAAAAAAAAAAAAAAAFAAAABQAAAAAAAAC6AAAA" + 558 "BQAAABT/////XgQawP////+epkig/////5+7FZD/////oIYqoP////+hmveQ////" + 559 "/8uJGqD/////0iP0cP/////SYSYQ/////9b+dCD/////2ICtkP/////a/tGg////" + 560 "/9vAkBD/////3N6zoP/////dqayQ/////96+laD/////34mOkP/////gnneg////" + 561 "/+FpcJD/////4n5ZoP/////jSVKQ/////+ReO6D/////5Sk0kP/////mR1gg////" + 562 "/+cSURD/////6Cc6IP/////o8jMQ/////+oHHCD/////6tIVEP/////r5v4g////" + 563 "/+yx9xD/////7cbgIP/////ukdkQ/////++v/KD/////8HG7EP/////xj96g////" + 564 "//J/wZD/////82/AoP/////0X6OQ//////VPoqD/////9j+FkP/////3L4Sg////" + 565 "//goohD/////+Q9moP/////6CIQQ//////r4gyD/////++hmEP/////82GUg////" + 566 "//3ISBD//////rhHIP//////qCoQAAAAAACYKSAAAAAAAYgMEAAAAAACeAsgAAAA" + 567 "AANxKJAAAAAABGEnoAAAAAAFUQqQAAAAAAZBCaAAAAAABzDskAAAAAAHjUOgAAAA" + 568 "AAkQzpAAAAAACa2/IAAAAAAK8LCQAAAAAAvgr6AAAAAADNnNEAAAAAANwJGgAAAA" + 569 "AA65rxAAAAAAD6muIAAAAAAQmZEQAAAAABGJkCAAAAAAEnlzEAAAAAATaXIgAAAA" + 570 "ABRZVRAAAAAAFUlUIAAAAAAWOTcQAAAAABcpNiAAAAAAGCJTkAAAAAAZCRggAAAA" + 571 "ABoCNZAAAAAAGvI0oAAAAAAb4heQAAAAABzSFqAAAAAAHcH5kAAAAAAesfigAAAA" + 572 "AB+h25AAAAAAIHYrIAAAAAAhgb2QAAAAACJWDSAAAAAAI2raEAAAAAAkNe8gAAAA" + 573 "ACVKvBAAAAAAJhXRIAAAAAAnKp4QAAAAACf+7aAAAAAAKQqAEAAAAAAp3s+gAAAA" + 574 "ACrqYhAAAAAAK76xoAAAAAAs036QAAAAAC2ek6AAAAAALrNgkAAAAAAvfnWgAAAA" + 575 "ADCTQpAAAAAAMWeSIAAAAAAycySQAAAAADNHdCAAAAAANFMGkAAAAAA1J1YgAAAA" + 576 "ADYy6JAAAAAANwc4IAAAAAA4HAUQAAAAADjnGiAAAAAAOfvnEAAAAAA6xvwgAAAA" + 577 "ADvbyRAAAAAAPLAYoAAAAAA9u6sQAAAAAD6P+qAAAAAAP5uNEAAAAABAb9ygAAAA" + 578 "AEGEqZAAAAAAQk++oAAAAABDZIuQAAAAAEQvoKAAAAAARURtkAAAAABF89MgAAAA" + 579 "AEctihAAAAAAR9O1IAAAAABJDWwQAAAAAEmzlyAAAAAASu1OEAAAAABLnLOgAAAA" + 580 "AEzWapAAAAAATXyVoAAAAABOtkyQAAAAAE9cd6AAAAAAUJYukAAAAABRPFmgAAAA" + 581 "AFJ2EJAAAAAAUxw7oAAAAABUVfKQAAAAAFT8HaAAAAAAVjXUkAAAAABW5TogAAAA" + 582 "AFge8RAAAAAAWMUcIAAAAABZ/tMQAAAAAFqk/iAAAAAAW961EAAAAABchOAgAAAA" + 583 "AF2+lxAAAAAAXmTCIAAAAABfnnkQAAAAAGBN3qAAAAAAYYeVkAAAAABiLcCgAAAA" + 584 "AGNnd5AAAAAAZA2ioAAAAABlR1mQAAAAAGXthKAAAAAAZyc7kAAAAABnzWagAAAA" + 585 "AGkHHZAAAAAAaa1IoAAAAABq5v+QAAAAAGuWZSAAAAAAbNAcEAAAAABtdkcgAAAA" + 586 "AG6v/hAAAAAAb1YpIAAAAABwj+AQAAAAAHE2CyAAAAAAcm/CEAAAAABzFe0gAAAA" + 587 "AHRPpBAAAAAAdP8JoAAAAAB2OMCQAAAAAHbe66AAAAAAeBiikAAAAAB4vs2gAAAA" + 588 "AHn4hJAAAAAAep6voAAAAAB72GaQAAAAAHx+kaAAAAAAfbhIkAAAAAB+XnOgAAAA" + 589 "AH+YKpACAQIBAgMEAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIB" + 590 "AgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIB" + 591 "AgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIB" + 592 "AgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQIBAgECAQL/" + 593 "/5EmAAD//52QAQT//4+AAAj//52QAQz//52QARBMTVQAUERUAFBTVABQV1QAUFBU" + 594 "AAAAAAABAAAAAAEKUFNUOFBEVCxNMy4yLjAsTTExLjEuMAo="