summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 02:37:40 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 02:37:40 +0000
commit9ad5c7b5e23b4940e7a3ea3ca3a6fb77e6a8fab0 (patch)
treed088b5210e77d9fa91d954d8550e00e372b47378 /plugins
downloadktorrent-9ad5c7b5e23b4940e7a3ea3ca3a6fb77e6a8fab0.tar.gz
ktorrent-9ad5c7b5e23b4940e7a3ea3ca3a6fb77e6a8fab0.zip
Updated to final KDE3 ktorrent release (2.2.6)
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/ktorrent@1077377 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Makefile.am4
-rw-r--r--plugins/infowidget/GeoIP.c825
-rw-r--r--plugins/infowidget/GeoIP.h161
-rw-r--r--plugins/infowidget/Makefile.am40
-rw-r--r--plugins/infowidget/availabilitychunkbar.cpp50
-rw-r--r--plugins/infowidget/availabilitychunkbar.h43
-rw-r--r--plugins/infowidget/chunkbar.cpp312
-rw-r--r--plugins/infowidget/chunkbar.h77
-rw-r--r--plugins/infowidget/chunkdownloadview.cpp166
-rw-r--r--plugins/infowidget/chunkdownloadview.h70
-rw-r--r--plugins/infowidget/chunkdownloadviewbase.ui345
-rw-r--r--plugins/infowidget/downloadedchunkbar.cpp47
-rw-r--r--plugins/infowidget/downloadedchunkbar.h42
-rw-r--r--plugins/infowidget/fileview.cpp447
-rw-r--r--plugins/infowidget/fileview.h76
-rw-r--r--plugins/infowidget/flagdb.cpp119
-rw-r--r--plugins/infowidget/flagdb.h68
-rw-r--r--plugins/infowidget/floatspinbox.cpp226
-rw-r--r--plugins/infowidget/floatspinbox.h93
-rw-r--r--plugins/infowidget/geoip/FLAGS_LICENCE2
-rw-r--r--plugins/infowidget/geoip/GeoIP-LICENSE.txt74
-rw-r--r--plugins/infowidget/geoip/Makefile.am41
-rw-r--r--plugins/infowidget/geoip/ad.pngbin0 -> 569 bytes
-rw-r--r--plugins/infowidget/geoip/ae.pngbin0 -> 410 bytes
-rw-r--r--plugins/infowidget/geoip/af.pngbin0 -> 593 bytes
-rw-r--r--plugins/infowidget/geoip/ag.pngbin0 -> 669 bytes
-rw-r--r--plugins/infowidget/geoip/ai.pngbin0 -> 669 bytes
-rw-r--r--plugins/infowidget/geoip/al.pngbin0 -> 571 bytes
-rw-r--r--plugins/infowidget/geoip/am.pngbin0 -> 573 bytes
-rw-r--r--plugins/infowidget/geoip/an.pngbin0 -> 577 bytes
-rw-r--r--plugins/infowidget/geoip/ao.pngbin0 -> 587 bytes
-rw-r--r--plugins/infowidget/geoip/aq.pngbin0 -> 609 bytes
-rw-r--r--plugins/infowidget/geoip/ar.pngbin0 -> 577 bytes
-rw-r--r--plugins/infowidget/geoip/as.pngbin0 -> 652 bytes
-rw-r--r--plugins/infowidget/geoip/at.pngbin0 -> 550 bytes
-rw-r--r--plugins/infowidget/geoip/au.pngbin0 -> 633 bytes
-rw-r--r--plugins/infowidget/geoip/aw.pngbin0 -> 572 bytes
-rw-r--r--plugins/infowidget/geoip/az.pngbin0 -> 602 bytes
-rw-r--r--plugins/infowidget/geoip/ba.pngbin0 -> 618 bytes
-rw-r--r--plugins/infowidget/geoip/bb.pngbin0 -> 573 bytes
-rw-r--r--plugins/infowidget/geoip/bd.pngbin0 -> 571 bytes
-rw-r--r--plugins/infowidget/geoip/be.pngbin0 -> 533 bytes
-rw-r--r--plugins/infowidget/geoip/bf.pngbin0 -> 567 bytes
-rw-r--r--plugins/infowidget/geoip/bg.pngbin0 -> 554 bytes
-rw-r--r--plugins/infowidget/geoip/bh.pngbin0 -> 513 bytes
-rw-r--r--plugins/infowidget/geoip/bi.pngbin0 -> 667 bytes
-rw-r--r--plugins/infowidget/geoip/bj.pngbin0 -> 543 bytes
-rw-r--r--plugins/infowidget/geoip/bm.pngbin0 -> 614 bytes
-rw-r--r--plugins/infowidget/geoip/bn.pngbin0 -> 632 bytes
-rw-r--r--plugins/infowidget/geoip/bo.pngbin0 -> 582 bytes
-rw-r--r--plugins/infowidget/geoip/br.pngbin0 -> 664 bytes
-rw-r--r--plugins/infowidget/geoip/bs.pngbin0 -> 605 bytes
-rw-r--r--plugins/infowidget/geoip/bt.pngbin0 -> 613 bytes
-rw-r--r--plugins/infowidget/geoip/bv.pngbin0 -> 603 bytes
-rw-r--r--plugins/infowidget/geoip/bw.pngbin0 -> 565 bytes
-rw-r--r--plugins/infowidget/geoip/by.pngbin0 -> 555 bytes
-rw-r--r--plugins/infowidget/geoip/bz.pngbin0 -> 617 bytes
-rw-r--r--plugins/infowidget/geoip/ca.pngbin0 -> 605 bytes
-rw-r--r--plugins/infowidget/geoip/cc.pngbin0 -> 667 bytes
-rw-r--r--plugins/infowidget/geoip/cd.pngbin0 -> 607 bytes
-rw-r--r--plugins/infowidget/geoip/cf.pngbin0 -> 610 bytes
-rw-r--r--plugins/infowidget/geoip/cg.pngbin0 -> 557 bytes
-rw-r--r--plugins/infowidget/geoip/ch.pngbin0 -> 580 bytes
-rw-r--r--plugins/infowidget/geoip/ci.pngbin0 -> 532 bytes
-rw-r--r--plugins/infowidget/geoip/ck.pngbin0 -> 605 bytes
-rw-r--r--plugins/infowidget/geoip/cl.pngbin0 -> 480 bytes
-rw-r--r--plugins/infowidget/geoip/cm.pngbin0 -> 547 bytes
-rw-r--r--plugins/infowidget/geoip/cn.pngbin0 -> 481 bytes
-rw-r--r--plugins/infowidget/geoip/co.pngbin0 -> 572 bytes
-rw-r--r--plugins/infowidget/geoip/cr.pngbin0 -> 592 bytes
-rw-r--r--plugins/infowidget/geoip/cs.pngbin0 -> 441 bytes
-rw-r--r--plugins/infowidget/geoip/cu.pngbin0 -> 623 bytes
-rw-r--r--plugins/infowidget/geoip/cv.pngbin0 -> 602 bytes
-rw-r--r--plugins/infowidget/geoip/cx.pngbin0 -> 626 bytes
-rw-r--r--plugins/infowidget/geoip/cy.pngbin0 -> 563 bytes
-rw-r--r--plugins/infowidget/geoip/cz.pngbin0 -> 581 bytes
-rw-r--r--plugins/infowidget/geoip/de.pngbin0 -> 539 bytes
-rw-r--r--plugins/infowidget/geoip/dj.pngbin0 -> 508 bytes
-rw-r--r--plugins/infowidget/geoip/dk.pngbin0 -> 561 bytes
-rw-r--r--plugins/infowidget/geoip/dm.pngbin0 -> 680 bytes
-rw-r--r--plugins/infowidget/geoip/do.pngbin0 -> 606 bytes
-rw-r--r--plugins/infowidget/geoip/dz.pngbin0 -> 566 bytes
-rw-r--r--plugins/infowidget/geoip/ec.pngbin0 -> 606 bytes
-rw-r--r--plugins/infowidget/geoip/ee.pngbin0 -> 542 bytes
-rw-r--r--plugins/infowidget/geoip/eg.pngbin0 -> 558 bytes
-rw-r--r--plugins/infowidget/geoip/eh.pngbin0 -> 551 bytes
-rw-r--r--plugins/infowidget/geoip/er.pngbin0 -> 605 bytes
-rw-r--r--plugins/infowidget/geoip/es.pngbin0 -> 588 bytes
-rw-r--r--plugins/infowidget/geoip/et.pngbin0 -> 606 bytes
-rw-r--r--plugins/infowidget/geoip/eu.pngbin0 -> 631 bytes
-rw-r--r--plugins/infowidget/geoip/fi.pngbin0 -> 568 bytes
-rw-r--r--plugins/infowidget/geoip/fj.pngbin0 -> 611 bytes
-rw-r--r--plugins/infowidget/geoip/fk.pngbin0 -> 683 bytes
-rw-r--r--plugins/infowidget/geoip/fm.pngbin0 -> 571 bytes
-rw-r--r--plugins/infowidget/geoip/fo.pngbin0 -> 582 bytes
-rw-r--r--plugins/infowidget/geoip/fr.pngbin0 -> 521 bytes
-rw-r--r--plugins/infowidget/geoip/ga.pngbin0 -> 579 bytes
-rw-r--r--plugins/infowidget/geoip/gb.pngbin0 -> 678 bytes
-rw-r--r--plugins/infowidget/geoip/gd.pngbin0 -> 598 bytes
-rw-r--r--plugins/infowidget/geoip/ge.pngbin0 -> 494 bytes
-rw-r--r--plugins/infowidget/geoip/geoip.datbin0 -> 1025467 bytes
-rw-r--r--plugins/infowidget/geoip/gf.pngbin0 -> 513 bytes
-rw-r--r--plugins/infowidget/geoip/gh.pngbin0 -> 455 bytes
-rw-r--r--plugins/infowidget/geoip/gi.pngbin0 -> 560 bytes
-rw-r--r--plugins/infowidget/geoip/gl.pngbin0 -> 573 bytes
-rw-r--r--plugins/infowidget/geoip/gm.pngbin0 -> 456 bytes
-rw-r--r--plugins/infowidget/geoip/gn.pngbin0 -> 542 bytes
-rw-r--r--plugins/infowidget/geoip/gp.pngbin0 -> 576 bytes
-rw-r--r--plugins/infowidget/geoip/gq.pngbin0 -> 584 bytes
-rw-r--r--plugins/infowidget/geoip/gr.pngbin0 -> 584 bytes
-rw-r--r--plugins/infowidget/geoip/gs.pngbin0 -> 660 bytes
-rw-r--r--plugins/infowidget/geoip/gt.pngbin0 -> 594 bytes
-rw-r--r--plugins/infowidget/geoip/gu.pngbin0 -> 578 bytes
-rw-r--r--plugins/infowidget/geoip/gw.pngbin0 -> 460 bytes
-rw-r--r--plugins/infowidget/geoip/gy.pngbin0 -> 534 bytes
-rw-r--r--plugins/infowidget/geoip/hk.pngbin0 -> 608 bytes
-rw-r--r--plugins/infowidget/geoip/hm.pngbin0 -> 650 bytes
-rw-r--r--plugins/infowidget/geoip/hn.pngbin0 -> 577 bytes
-rw-r--r--plugins/infowidget/geoip/hr.pngbin0 -> 603 bytes
-rw-r--r--plugins/infowidget/geoip/ht.pngbin0 -> 586 bytes
-rw-r--r--plugins/infowidget/geoip/hu.pngbin0 -> 551 bytes
-rw-r--r--plugins/infowidget/geoip/id.pngbin0 -> 534 bytes
-rw-r--r--plugins/infowidget/geoip/ie.pngbin0 -> 517 bytes
-rw-r--r--plugins/infowidget/geoip/il.pngbin0 -> 589 bytes
-rw-r--r--plugins/infowidget/geoip/in.pngbin0 -> 577 bytes
-rw-r--r--plugins/infowidget/geoip/io.pngbin0 -> 759 bytes
-rw-r--r--plugins/infowidget/geoip/iq.pngbin0 -> 582 bytes
-rw-r--r--plugins/infowidget/geoip/ir.pngbin0 -> 572 bytes
-rw-r--r--plugins/infowidget/geoip/is.pngbin0 -> 607 bytes
-rw-r--r--plugins/infowidget/geoip/it.pngbin0 -> 503 bytes
-rw-r--r--plugins/infowidget/geoip/jm.pngbin0 -> 662 bytes
-rw-r--r--plugins/infowidget/geoip/jo.pngbin0 -> 596 bytes
-rw-r--r--plugins/infowidget/geoip/jp.pngbin0 -> 555 bytes
-rw-r--r--plugins/infowidget/geoip/ke.pngbin0 -> 613 bytes
-rw-r--r--plugins/infowidget/geoip/kg.pngbin0 -> 562 bytes
-rw-r--r--plugins/infowidget/geoip/kh.pngbin0 -> 605 bytes
-rw-r--r--plugins/infowidget/geoip/ki.pngbin0 -> 607 bytes
-rw-r--r--plugins/infowidget/geoip/km.pngbin0 -> 561 bytes
-rw-r--r--plugins/infowidget/geoip/kn.pngbin0 -> 610 bytes
-rw-r--r--plugins/infowidget/geoip/kp.pngbin0 -> 623 bytes
-rw-r--r--plugins/infowidget/geoip/kr.pngbin0 -> 610 bytes
-rw-r--r--plugins/infowidget/geoip/kw.pngbin0 -> 455 bytes
-rw-r--r--plugins/infowidget/geoip/ky.pngbin0 -> 604 bytes
-rw-r--r--plugins/infowidget/geoip/kz.pngbin0 -> 602 bytes
-rw-r--r--plugins/infowidget/geoip/la.pngbin0 -> 527 bytes
-rw-r--r--plugins/infowidget/geoip/lb.pngbin0 -> 593 bytes
-rw-r--r--plugins/infowidget/geoip/lc.pngbin0 -> 612 bytes
-rw-r--r--plugins/infowidget/geoip/li.pngbin0 -> 480 bytes
-rw-r--r--plugins/infowidget/geoip/lk.pngbin0 -> 598 bytes
-rw-r--r--plugins/infowidget/geoip/lr.pngbin0 -> 566 bytes
-rw-r--r--plugins/infowidget/geoip/ls.pngbin0 -> 667 bytes
-rw-r--r--plugins/infowidget/geoip/lt.pngbin0 -> 530 bytes
-rw-r--r--plugins/infowidget/geoip/lu.pngbin0 -> 553 bytes
-rw-r--r--plugins/infowidget/geoip/lv.pngbin0 -> 564 bytes
-rw-r--r--plugins/infowidget/geoip/ly.pngbin0 -> 462 bytes
-rw-r--r--plugins/infowidget/geoip/ma.pngbin0 -> 526 bytes
-rw-r--r--plugins/infowidget/geoip/mc.pngbin0 -> 518 bytes
-rw-r--r--plugins/infowidget/geoip/md.pngbin0 -> 576 bytes
-rw-r--r--plugins/infowidget/geoip/mg.pngbin0 -> 547 bytes
-rw-r--r--plugins/infowidget/geoip/mh.pngbin0 -> 679 bytes
-rw-r--r--plugins/infowidget/geoip/mk.pngbin0 -> 656 bytes
-rw-r--r--plugins/infowidget/geoip/ml.pngbin0 -> 435 bytes
-rw-r--r--plugins/infowidget/geoip/mm.pngbin0 -> 527 bytes
-rw-r--r--plugins/infowidget/geoip/mn.pngbin0 -> 559 bytes
-rw-r--r--plugins/infowidget/geoip/mo.pngbin0 -> 589 bytes
-rw-r--r--plugins/infowidget/geoip/mp.pngbin0 -> 622 bytes
-rw-r--r--plugins/infowidget/geoip/mq.pngbin0 -> 513 bytes
-rw-r--r--plugins/infowidget/geoip/mr.pngbin0 -> 509 bytes
-rw-r--r--plugins/infowidget/geoip/ms.pngbin0 -> 616 bytes
-rw-r--r--plugins/infowidget/geoip/mt.pngbin0 -> 496 bytes
-rw-r--r--plugins/infowidget/geoip/mu.pngbin0 -> 418 bytes
-rw-r--r--plugins/infowidget/geoip/mv.pngbin0 -> 589 bytes
-rw-r--r--plugins/infowidget/geoip/mw.pngbin0 -> 462 bytes
-rw-r--r--plugins/infowidget/geoip/mx.pngbin0 -> 605 bytes
-rw-r--r--plugins/infowidget/geoip/my.pngbin0 -> 596 bytes
-rw-r--r--plugins/infowidget/geoip/mz.pngbin0 -> 601 bytes
-rw-r--r--plugins/infowidget/geoip/na.pngbin0 -> 652 bytes
-rw-r--r--plugins/infowidget/geoip/nc.pngbin0 -> 620 bytes
-rw-r--r--plugins/infowidget/geoip/ne.pngbin0 -> 526 bytes
-rw-r--r--plugins/infowidget/geoip/nf.pngbin0 -> 582 bytes
-rw-r--r--plugins/infowidget/geoip/ng.pngbin0 -> 512 bytes
-rw-r--r--plugins/infowidget/geoip/ni.pngbin0 -> 523 bytes
-rw-r--r--plugins/infowidget/geoip/nl.pngbin0 -> 553 bytes
-rw-r--r--plugins/infowidget/geoip/no.pngbin0 -> 614 bytes
-rw-r--r--plugins/infowidget/geoip/np.pngbin0 -> 530 bytes
-rw-r--r--plugins/infowidget/geoip/nr.pngbin0 -> 577 bytes
-rw-r--r--plugins/infowidget/geoip/nu.pngbin0 -> 530 bytes
-rw-r--r--plugins/infowidget/geoip/nz.pngbin0 -> 605 bytes
-rw-r--r--plugins/infowidget/geoip/om.pngbin0 -> 550 bytes
-rw-r--r--plugins/infowidget/geoip/pa.pngbin0 -> 586 bytes
-rw-r--r--plugins/infowidget/geoip/pe.pngbin0 -> 582 bytes
-rw-r--r--plugins/infowidget/geoip/pf.pngbin0 -> 605 bytes
-rw-r--r--plugins/infowidget/geoip/pg.pngbin0 -> 556 bytes
-rw-r--r--plugins/infowidget/geoip/ph.pngbin0 -> 605 bytes
-rw-r--r--plugins/infowidget/geoip/pk.pngbin0 -> 615 bytes
-rw-r--r--plugins/infowidget/geoip/pl.pngbin0 -> 513 bytes
-rw-r--r--plugins/infowidget/geoip/pm.pngbin0 -> 652 bytes
-rw-r--r--plugins/infowidget/geoip/pn.pngbin0 -> 692 bytes
-rw-r--r--plugins/infowidget/geoip/pr.pngbin0 -> 606 bytes
-rw-r--r--plugins/infowidget/geoip/ps.pngbin0 -> 480 bytes
-rw-r--r--plugins/infowidget/geoip/pt.pngbin0 -> 572 bytes
-rw-r--r--plugins/infowidget/geoip/pw.pngbin0 -> 570 bytes
-rw-r--r--plugins/infowidget/geoip/py.pngbin0 -> 578 bytes
-rw-r--r--plugins/infowidget/geoip/qa.pngbin0 -> 519 bytes
-rw-r--r--plugins/infowidget/geoip/re.pngbin0 -> 513 bytes
-rw-r--r--plugins/infowidget/geoip/ro.pngbin0 -> 541 bytes
-rw-r--r--plugins/infowidget/geoip/ru.pngbin0 -> 549 bytes
-rw-r--r--plugins/infowidget/geoip/rw.pngbin0 -> 470 bytes
-rw-r--r--plugins/infowidget/geoip/sa.pngbin0 -> 557 bytes
-rw-r--r--plugins/infowidget/geoip/sb.pngbin0 -> 636 bytes
-rw-r--r--plugins/infowidget/geoip/sc.pngbin0 -> 645 bytes
-rw-r--r--plugins/infowidget/geoip/sd.pngbin0 -> 578 bytes
-rw-r--r--plugins/infowidget/geoip/se.pngbin0 -> 607 bytes
-rw-r--r--plugins/infowidget/geoip/sg.pngbin0 -> 556 bytes
-rw-r--r--plugins/infowidget/geoip/sh.pngbin0 -> 631 bytes
-rw-r--r--plugins/infowidget/geoip/si.pngbin0 -> 575 bytes
-rw-r--r--plugins/infowidget/geoip/sj.pngbin0 -> 603 bytes
-rw-r--r--plugins/infowidget/geoip/sk.pngbin0 -> 595 bytes
-rw-r--r--plugins/infowidget/geoip/sl.pngbin0 -> 559 bytes
-rw-r--r--plugins/infowidget/geoip/sm.pngbin0 -> 590 bytes
-rw-r--r--plugins/infowidget/geoip/sn.pngbin0 -> 493 bytes
-rw-r--r--plugins/infowidget/geoip/so.pngbin0 -> 557 bytes
-rw-r--r--plugins/infowidget/geoip/sr.pngbin0 -> 524 bytes
-rw-r--r--plugins/infowidget/geoip/st.pngbin0 -> 525 bytes
-rw-r--r--plugins/infowidget/geoip/sv.pngbin0 -> 542 bytes
-rw-r--r--plugins/infowidget/geoip/sy.pngbin0 -> 447 bytes
-rw-r--r--plugins/infowidget/geoip/sz.pngbin0 -> 722 bytes
-rw-r--r--plugins/infowidget/geoip/tc.pngbin0 -> 608 bytes
-rw-r--r--plugins/infowidget/geoip/td.pngbin0 -> 559 bytes
-rw-r--r--plugins/infowidget/geoip/tf.pngbin0 -> 547 bytes
-rw-r--r--plugins/infowidget/geoip/tg.pngbin0 -> 595 bytes
-rw-r--r--plugins/infowidget/geoip/th.pngbin0 -> 573 bytes
-rw-r--r--plugins/infowidget/geoip/tj.pngbin0 -> 523 bytes
-rw-r--r--plugins/infowidget/geoip/tk.pngbin0 -> 684 bytes
-rw-r--r--plugins/infowidget/geoip/tl.pngbin0 -> 519 bytes
-rw-r--r--plugins/infowidget/geoip/tm.pngbin0 -> 547 bytes
-rw-r--r--plugins/infowidget/geoip/tn.pngbin0 -> 600 bytes
-rw-r--r--plugins/infowidget/geoip/to.pngbin0 -> 542 bytes
-rw-r--r--plugins/infowidget/geoip/tp.pngbin0 -> 581 bytes
-rw-r--r--plugins/infowidget/geoip/tr.pngbin0 -> 550 bytes
-rw-r--r--plugins/infowidget/geoip/tt.pngbin0 -> 663 bytes
-rw-r--r--plugins/infowidget/geoip/tv.pngbin0 -> 605 bytes
-rw-r--r--plugins/infowidget/geoip/tw.pngbin0 -> 524 bytes
-rw-r--r--plugins/infowidget/geoip/tz.pngbin0 -> 620 bytes
-rw-r--r--plugins/infowidget/geoip/ua.pngbin0 -> 550 bytes
-rw-r--r--plugins/infowidget/geoip/ug.pngbin0 -> 606 bytes
-rw-r--r--plugins/infowidget/geoip/um.pngbin0 -> 598 bytes
-rw-r--r--plugins/infowidget/geoip/us.pngbin0 -> 569 bytes
-rw-r--r--plugins/infowidget/geoip/uy.pngbin0 -> 559 bytes
-rw-r--r--plugins/infowidget/geoip/uz.pngbin0 -> 535 bytes
-rw-r--r--plugins/infowidget/geoip/va.pngbin0 -> 573 bytes
-rw-r--r--plugins/infowidget/geoip/vc.pngbin0 -> 547 bytes
-rw-r--r--plugins/infowidget/geoip/ve.pngbin0 -> 578 bytes
-rw-r--r--plugins/infowidget/geoip/vg.pngbin0 -> 612 bytes
-rw-r--r--plugins/infowidget/geoip/vi.pngbin0 -> 645 bytes
-rw-r--r--plugins/infowidget/geoip/vn.pngbin0 -> 565 bytes
-rw-r--r--plugins/infowidget/geoip/vu.pngbin0 -> 571 bytes
-rw-r--r--plugins/infowidget/geoip/wf.pngbin0 -> 513 bytes
-rw-r--r--plugins/infowidget/geoip/ws.pngbin0 -> 536 bytes
-rw-r--r--plugins/infowidget/geoip/ye.pngbin0 -> 542 bytes
-rw-r--r--plugins/infowidget/geoip/yt.pngbin0 -> 513 bytes
-rw-r--r--plugins/infowidget/geoip/yu.pngbin0 -> 566 bytes
-rw-r--r--plugins/infowidget/geoip/za.pngbin0 -> 661 bytes
-rw-r--r--plugins/infowidget/geoip/zm.pngbin0 -> 524 bytes
-rw-r--r--plugins/infowidget/geoip/zw.pngbin0 -> 617 bytes
-rw-r--r--plugins/infowidget/infowidgetplugin.cpp244
-rw-r--r--plugins/infowidget/infowidgetplugin.h77
-rw-r--r--plugins/infowidget/infowidgetpluginsettings.kcfgc7
-rw-r--r--plugins/infowidget/infowidgetprefpage.cpp74
-rw-r--r--plugins/infowidget/infowidgetprefpage.h52
-rw-r--r--plugins/infowidget/iwfiletreediritem.cpp224
-rw-r--r--plugins/infowidget/iwfiletreediritem.h80
-rw-r--r--plugins/infowidget/iwfiletreeitem.cpp166
-rw-r--r--plugins/infowidget/iwfiletreeitem.h64
-rw-r--r--plugins/infowidget/iwpref.ui69
-rw-r--r--plugins/infowidget/ktinfowidgetplugin.desktop29
-rw-r--r--plugins/infowidget/ktinfowidgetplugin.kcfg22
-rw-r--r--plugins/infowidget/ktorrentmonitor.cpp88
-rw-r--r--plugins/infowidget/ktorrentmonitor.h58
-rw-r--r--plugins/infowidget/localefloatvalidator.cpp39
-rw-r--r--plugins/infowidget/localefloatvalidator.h45
-rw-r--r--plugins/infowidget/peerview.cpp357
-rw-r--r--plugins/infowidget/peerview.h81
-rw-r--r--plugins/infowidget/statustab.cpp267
-rw-r--r--plugins/infowidget/statustab.h55
-rw-r--r--plugins/infowidget/statustabbase.ui667
-rw-r--r--plugins/infowidget/trackerview.cpp243
-rw-r--r--plugins/infowidget/trackerview.h62
-rw-r--r--plugins/infowidget/trackerviewbase.ui317
-rw-r--r--plugins/ipfilter/Makefile.am31
-rw-r--r--plugins/ipfilter/antip2p.cpp237
-rw-r--r--plugins/ipfilter/antip2p.h117
-rw-r--r--plugins/ipfilter/convert_dlg.ui157
-rw-r--r--plugins/ipfilter/convertdialog.cpp262
-rw-r--r--plugins/ipfilter/convertdialog.h52
-rw-r--r--plugins/ipfilter/ipblockingpref.ui204
-rw-r--r--plugins/ipfilter/ipblockingprefpage.cpp258
-rw-r--r--plugins/ipfilter/ipblockingprefpage.h83
-rw-r--r--plugins/ipfilter/ipfilterplugin.cpp129
-rw-r--r--plugins/ipfilter/ipfilterplugin.h73
-rw-r--r--plugins/ipfilter/ipfilterpluginsettings.kcfgc7
-rw-r--r--plugins/ipfilter/ktipfilterplugin.desktop56
-rw-r--r--plugins/ipfilter/ktipfilterplugin.kcfg18
-rw-r--r--plugins/logviewer/Makefile.am33
-rw-r--r--plugins/logviewer/ktlogviewerplugin.desktop26
-rw-r--r--plugins/logviewer/ktlogviewerplugin.kcfg71
-rw-r--r--plugins/logviewer/logflags.cpp170
-rw-r--r--plugins/logviewer/logflags.h91
-rw-r--r--plugins/logviewer/logprefpage.cpp63
-rw-r--r--plugins/logviewer/logprefpage.h51
-rw-r--r--plugins/logviewer/logprefwidget.cpp123
-rw-r--r--plugins/logviewer/logprefwidget.h39
-rw-r--r--plugins/logviewer/logprefwidgetbase.ui648
-rw-r--r--plugins/logviewer/logviewer.cpp110
-rw-r--r--plugins/logviewer/logviewer.h50
-rw-r--r--plugins/logviewer/logviewerplugin.cpp84
-rw-r--r--plugins/logviewer/logviewerplugin.h52
-rw-r--r--plugins/logviewer/logviewerpluginsettings.kcfgc7
-rw-r--r--plugins/partfileimport/Makefile.am27
-rw-r--r--plugins/partfileimport/importdialog.cpp389
-rw-r--r--plugins/partfileimport/importdialog.h78
-rw-r--r--plugins/partfileimport/importdlgbase.ui163
-rw-r--r--plugins/partfileimport/ktpartfileimportplugin.desktop25
-rw-r--r--plugins/partfileimport/ktpartfileimportpluginui.rc8
-rw-r--r--plugins/partfileimport/partfileimportplugin.cpp78
-rw-r--r--plugins/partfileimport/partfileimportplugin.h52
-rw-r--r--plugins/rssfeed/Makefile.am30
-rw-r--r--plugins/rssfeed/ktrssfeedplugin.desktop22
-rw-r--r--plugins/rssfeed/ktrssfeedplugin.kcfg14
-rw-r--r--plugins/rssfeed/rss/COPYING20
-rw-r--r--plugins/rssfeed/rss/Makefile.am20
-rw-r--r--plugins/rssfeed/rss/README6
-rw-r--r--plugins/rssfeed/rss/article.cpp270
-rw-r--r--plugins/rssfeed/rss/article.h159
-rw-r--r--plugins/rssfeed/rss/document.cpp619
-rw-r--r--plugins/rssfeed/rss/document.h237
-rw-r--r--plugins/rssfeed/rss/global.h145
-rw-r--r--plugins/rssfeed/rss/image.cpp167
-rw-r--r--plugins/rssfeed/rss/image.h173
-rw-r--r--plugins/rssfeed/rss/librss.doxyfile921
-rw-r--r--plugins/rssfeed/rss/librss.h22
-rw-r--r--plugins/rssfeed/rss/loader.cpp425
-rw-r--r--plugins/rssfeed/rss/loader.h339
-rw-r--r--plugins/rssfeed/rss/rss-faq.html396
-rw-r--r--plugins/rssfeed/rss/testlibrss.cpp75
-rw-r--r--plugins/rssfeed/rss/testlibrss.h25
-rw-r--r--plugins/rssfeed/rss/textinput.cpp96
-rw-r--r--plugins/rssfeed/rss/textinput.h121
-rw-r--r--plugins/rssfeed/rss/tools_p.cpp51
-rw-r--r--plugins/rssfeed/rss/tools_p.h34
-rw-r--r--plugins/rssfeed/rssarticle.cpp103
-rw-r--r--plugins/rssfeed/rssarticle.h82
-rw-r--r--plugins/rssfeed/rssfeed.cpp359
-rw-r--r--plugins/rssfeed/rssfeed.h127
-rw-r--r--plugins/rssfeed/rssfeedmanager.cpp1318
-rw-r--r--plugins/rssfeed/rssfeedmanager.h130
-rw-r--r--plugins/rssfeed/rssfeedplugin.cpp86
-rw-r--r--plugins/rssfeed/rssfeedplugin.h55
-rw-r--r--plugins/rssfeed/rssfeedwidget.ui969
-rw-r--r--plugins/rssfeed/rssfilter.cpp423
-rw-r--r--plugins/rssfeed/rssfilter.h151
-rw-r--r--plugins/rssfeed/rsslinkdownloader.cpp202
-rw-r--r--plugins/rssfeed/rsslinkdownloader.h82
-rw-r--r--plugins/scanfolder/Makefile.am31
-rw-r--r--plugins/scanfolder/ktscanfolderplugin.desktop26
-rw-r--r--plugins/scanfolder/ktscanfolderplugin.kcfg53
-rw-r--r--plugins/scanfolder/scanfolder.cpp273
-rw-r--r--plugins/scanfolder/scanfolder.h111
-rw-r--r--plugins/scanfolder/scanfolderplugin.cpp187
-rw-r--r--plugins/scanfolder/scanfolderplugin.h61
-rw-r--r--plugins/scanfolder/scanfolderpluginsettings.kcfgc7
-rw-r--r--plugins/scanfolder/scanfolderprefpage.cpp66
-rw-r--r--plugins/scanfolder/scanfolderprefpage.h53
-rw-r--r--plugins/scanfolder/scanfolderprefpagewidget.cpp107
-rw-r--r--plugins/scanfolder/scanfolderprefpagewidget.h36
-rw-r--r--plugins/scanfolder/sfprefwidgetbase.ui272
-rw-r--r--plugins/scheduler/Makefile.am36
-rw-r--r--plugins/scheduler/bwscheduler.cpp282
-rw-r--r--plugins/scheduler/bwscheduler.h173
-rw-r--r--plugins/scheduler/bwspage.ui877
-rw-r--r--plugins/scheduler/bwsprefpage.cpp0
-rw-r--r--plugins/scheduler/bwsprefpage.h0
-rw-r--r--plugins/scheduler/bwsprefpagewidget.cpp291
-rw-r--r--plugins/scheduler/bwsprefpagewidget.h84
-rw-r--r--plugins/scheduler/bwswidget.cpp334
-rw-r--r--plugins/scheduler/bwswidget.h107
-rw-r--r--plugins/scheduler/cell-a-0000.pngbin0 -> 700 bytes
-rw-r--r--plugins/scheduler/cell-a-0001.pngbin0 -> 1003 bytes
-rw-r--r--plugins/scheduler/cell-a-0002.pngbin0 -> 1102 bytes
-rw-r--r--plugins/scheduler/cell-a-0003.pngbin0 -> 1174 bytes
-rw-r--r--plugins/scheduler/cell-a-0004.pngbin0 -> 744 bytes
-rw-r--r--plugins/scheduler/cell-b-0000.pngbin0 -> 672 bytes
-rw-r--r--plugins/scheduler/cell-b-0001.pngbin0 -> 979 bytes
-rw-r--r--plugins/scheduler/cell-b-0002.pngbin0 -> 1063 bytes
-rw-r--r--plugins/scheduler/cell-b-0003.pngbin0 -> 1119 bytes
-rw-r--r--plugins/scheduler/cell-b-0004.pngbin0 -> 728 bytes
-rw-r--r--plugins/scheduler/ktschedulerplugin.desktop26
-rw-r--r--plugins/scheduler/ktschedulerplugin.kcfg18
-rw-r--r--plugins/scheduler/ktschedulerpluginui.rc12
-rw-r--r--plugins/scheduler/schedulerpage.ui146
-rw-r--r--plugins/scheduler/schedulerplugin.cpp152
-rw-r--r--plugins/scheduler/schedulerplugin.h70
-rw-r--r--plugins/scheduler/schedulerpluginsettings.kcfgc7
-rw-r--r--plugins/scheduler/schedulerprefpage.cpp64
-rw-r--r--plugins/scheduler/schedulerprefpage.h53
-rw-r--r--plugins/scheduler/schedulerprefpagewidget.cpp83
-rw-r--r--plugins/scheduler/schedulerprefpagewidget.h50
-rw-r--r--plugins/search/Makefile.am28
-rw-r--r--plugins/search/htmlpart.cpp198
-rw-r--r--plugins/search/htmlpart.h72
-rw-r--r--plugins/search/ktsearchplugin.desktop60
-rw-r--r--plugins/search/ktsearchplugin.kcfg30
-rw-r--r--plugins/search/searchbar.ui99
-rw-r--r--plugins/search/searchenginelist.cpp134
-rw-r--r--plugins/search/searchenginelist.h60
-rw-r--r--plugins/search/searchplugin.cpp157
-rw-r--r--plugins/search/searchplugin.h66
-rw-r--r--plugins/search/searchpluginsettings.kcfgc7
-rw-r--r--plugins/search/searchpref.ui320
-rw-r--r--plugins/search/searchprefpage.cpp289
-rw-r--r--plugins/search/searchprefpage.h78
-rw-r--r--plugins/search/searchtab.cpp169
-rw-r--r--plugins/search/searchtab.h77
-rw-r--r--plugins/search/searchwidget.cpp272
-rw-r--r--plugins/search/searchwidget.h89
-rw-r--r--plugins/stats/ChartDrawer.cc473
-rw-r--r--plugins/stats/ChartDrawer.h281
-rw-r--r--plugins/stats/ChartDrawerData.cc100
-rw-r--r--plugins/stats/ChartDrawerData.h123
-rw-r--r--plugins/stats/Makefile.am18
-rw-r--r--plugins/stats/PeerMonitor.cc187
-rw-r--r--plugins/stats/PeerMonitor.h123
-rw-r--r--plugins/stats/StatsCon.cc113
-rw-r--r--plugins/stats/StatsCon.h94
-rw-r--r--plugins/stats/StatsPluginPrefs.cc88
-rw-r--r--plugins/stats/StatsPluginPrefs.h66
-rw-r--r--plugins/stats/StatsPluginPrefsPage.cc29
-rw-r--r--plugins/stats/StatsPluginPrefsPage.h44
-rw-r--r--plugins/stats/StatsSpd.cc138
-rw-r--r--plugins/stats/StatsSpd.h113
-rw-r--r--plugins/stats/ktstatsplugin.desktop24
-rw-r--r--plugins/stats/ktstatsplugin.kcfg60
-rw-r--r--plugins/stats/sprefwgt.ui517
-rw-r--r--plugins/stats/statsconwgt.ui48
-rw-r--r--plugins/stats/statsplugin.cc321
-rw-r--r--plugins/stats/statsplugin.h153
-rw-r--r--plugins/stats/statspluginsettings.kcfgc7
-rw-r--r--plugins/stats/statsspdwgt.ui56
-rw-r--r--plugins/upnp/Makefile.am38
-rw-r--r--plugins/upnp/ktupnpplugin.desktop26
-rw-r--r--plugins/upnp/ktupnpplugin.kcfg13
-rw-r--r--plugins/upnp/soap.cpp53
-rw-r--r--plugins/upnp/soap.h62
-rw-r--r--plugins/upnp/upnpdescriptionparser.cpp220
-rw-r--r--plugins/upnp/upnpdescriptionparser.h49
-rw-r--r--plugins/upnp/upnpmcastsocket.cpp312
-rw-r--r--plugins/upnp/upnpmcastsocket.h91
-rw-r--r--plugins/upnp/upnpplugin.cpp95
-rw-r--r--plugins/upnp/upnpplugin.h51
-rw-r--r--plugins/upnp/upnppluginsettings.kcfgc7
-rw-r--r--plugins/upnp/upnpprefpage.cpp67
-rw-r--r--plugins/upnp/upnpprefpage.h58
-rw-r--r--plugins/upnp/upnpprefwidget.cpp253
-rw-r--r--plugins/upnp/upnpprefwidget.h83
-rw-r--r--plugins/upnp/upnprouter.cpp459
-rw-r--r--plugins/upnp/upnprouter.h223
-rw-r--r--plugins/upnp/upnpwidget.ui139
-rw-r--r--plugins/webinterface/Makefile.am34
-rw-r--r--plugins/webinterface/httpclienthandler.cpp237
-rw-r--r--plugins/webinterface/httpclienthandler.h86
-rw-r--r--plugins/webinterface/httpresponseheader.cpp78
-rw-r--r--plugins/webinterface/httpresponseheader.h51
-rw-r--r--plugins/webinterface/httpserver.cpp553
-rw-r--r--plugins/webinterface/httpserver.h104
-rw-r--r--plugins/webinterface/ktwebinterfaceplugin.desktop22
-rw-r--r--plugins/webinterface/ktwebinterfaceplugin.kcfg41
-rw-r--r--plugins/webinterface/php_handler.cpp121
-rw-r--r--plugins/webinterface/php_handler.h57
-rw-r--r--plugins/webinterface/php_interface.cpp486
-rw-r--r--plugins/webinterface/php_interface.h68
-rw-r--r--plugins/webinterface/webinterfaceplugin.cpp128
-rw-r--r--plugins/webinterface/webinterfaceplugin.h54
-rw-r--r--plugins/webinterface/webinterfacepluginsettings.kcfgc7
-rw-r--r--plugins/webinterface/webinterfacepref.ui256
-rw-r--r--plugins/webinterface/webinterfaceprefpage.cpp60
-rw-r--r--plugins/webinterface/webinterfaceprefpage.h55
-rw-r--r--plugins/webinterface/webinterfaceprefwidget.cpp137
-rw-r--r--plugins/webinterface/webinterfaceprefwidget.h41
-rw-r--r--plugins/webinterface/www/Makefile.am5
-rw-r--r--plugins/webinterface/www/coldmilk/Makefile.am8
-rw-r--r--plugins/webinterface/www/coldmilk/favicon.icobin0 -> 1406 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icon.pngbin0 -> 26977 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/16x16/Makefile.am6
-rw-r--r--plugins/webinterface/www/coldmilk/icons/16x16/edit_user.pngbin0 -> 954 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/16x16/high_priority.pngbin0 -> 232 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/16x16/low_priority.pngbin0 -> 242 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/16x16/normal_priority.pngbin0 -> 215 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/16x16/only_seed.pngbin0 -> 237 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/22x22/Makefile.am6
-rw-r--r--plugins/webinterface/www/coldmilk/icons/22x22/exit.pngbin0 -> 1397 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/22x22/ktstart_all.pngbin0 -> 1399 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/22x22/ktstop_all.pngbin0 -> 1128 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/22x22/remove.pngbin0 -> 1526 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/22x22/start.pngbin0 -> 1232 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/22x22/stop.pngbin0 -> 963 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/32x32/Makefile.am6
-rw-r--r--plugins/webinterface/www/coldmilk/icons/32x32/configure.pngbin0 -> 1838 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/32x32/extender_opened.pngbin0 -> 1627 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/32x32/fileopen.pngbin0 -> 2231 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/32x32/folder1.pngbin0 -> 2698 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/48x48/Makefile.am6
-rw-r--r--plugins/webinterface/www/coldmilk/icons/48x48/exit.pngbin0 -> 3109 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/48x48/switchuser.pngbin0 -> 3164 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/64x64/Makefile.am6
-rw-r--r--plugins/webinterface/www/coldmilk/icons/64x64/down.pngbin0 -> 5486 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/64x64/folder1_man.pngbin0 -> 6388 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/64x64/looknfeel.pngbin0 -> 5512 bytes
-rw-r--r--plugins/webinterface/www/coldmilk/icons/Makefile.am1
-rw-r--r--plugins/webinterface/www/coldmilk/interface.js44
-rw-r--r--plugins/webinterface/www/coldmilk/interface.php251
-rw-r--r--plugins/webinterface/www/coldmilk/login.html55
-rw-r--r--plugins/webinterface/www/coldmilk/page_update.js429
-rw-r--r--plugins/webinterface/www/coldmilk/rest.php265
-rw-r--r--plugins/webinterface/www/coldmilk/shutdown.php21
-rw-r--r--plugins/webinterface/www/coldmilk/style.css212
-rw-r--r--plugins/webinterface/www/default/Makefile.am9
-rw-r--r--plugins/webinterface/www/default/details.php89
-rw-r--r--plugins/webinterface/www/default/favicon.icobin0 -> 1406 bytes
-rw-r--r--plugins/webinterface/www/default/grad1.jpgbin0 -> 367 bytes
-rw-r--r--plugins/webinterface/www/default/grad2.jpgbin0 -> 362 bytes
-rw-r--r--plugins/webinterface/www/default/header_tile.pngbin0 -> 180 bytes
-rw-r--r--plugins/webinterface/www/default/high_priority.pngbin0 -> 232 bytes
-rw-r--r--plugins/webinterface/www/default/icon.pngbin0 -> 26977 bytes
-rw-r--r--plugins/webinterface/www/default/interface.php152
-rw-r--r--plugins/webinterface/www/default/ktorrentwebinterfacelogo.pngbin0 -> 97320 bytes
-rw-r--r--plugins/webinterface/www/default/login.html29
-rw-r--r--plugins/webinterface/www/default/low_priority.pngbin0 -> 242 bytes
-rw-r--r--plugins/webinterface/www/default/menu_bg.pngbin0 -> 249 bytes
-rw-r--r--plugins/webinterface/www/default/normal_priority.pngbin0 -> 215 bytes
-rw-r--r--plugins/webinterface/www/default/only_seed.pngbin0 -> 237 bytes
-rw-r--r--plugins/webinterface/www/default/remove.pngbin0 -> 1039 bytes
-rw-r--r--plugins/webinterface/www/default/shutdown.php13
-rw-r--r--plugins/webinterface/www/default/start.pngbin0 -> 868 bytes
-rw-r--r--plugins/webinterface/www/default/stop.pngbin0 -> 711 bytes
-rw-r--r--plugins/webinterface/www/default/style.css13
-rw-r--r--plugins/webinterface/www/default/stylen.css164
-rw-r--r--plugins/webinterface/www/default/wz_tooltip.js509
-rw-r--r--plugins/webinterface/www/mobile/Makefile.am8
-rw-r--r--plugins/webinterface/www/mobile/favicon.icobin0 -> 1406 bytes
-rw-r--r--plugins/webinterface/www/mobile/interface.php113
-rw-r--r--plugins/webinterface/www/mobile/ktorrentwebinterfacelogo.pngbin0 -> 97320 bytes
-rw-r--r--plugins/webinterface/www/mobile/login.html24
-rw-r--r--plugins/webinterface/www/mobile/remove.pngbin0 -> 1039 bytes
-rw-r--r--plugins/webinterface/www/mobile/settings.php44
-rw-r--r--plugins/webinterface/www/mobile/start.pngbin0 -> 868 bytes
-rw-r--r--plugins/webinterface/www/mobile/stop.pngbin0 -> 711 bytes
-rw-r--r--plugins/webinterface/www/mobile/torrent.php91
-rw-r--r--plugins/zeroconf/Makefile.am22
-rw-r--r--plugins/zeroconf/avahiservice.cpp341
-rw-r--r--plugins/zeroconf/avahiservice.h113
-rw-r--r--plugins/zeroconf/ktzeroconfplugin.desktop22
-rw-r--r--plugins/zeroconf/localbrowser.cpp47
-rw-r--r--plugins/zeroconf/localbrowser.h39
-rw-r--r--plugins/zeroconf/zeroconfplugin.cpp135
-rw-r--r--plugins/zeroconf/zeroconfplugin.h71
565 files changed, 37184 insertions, 0 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
new file mode 100644
index 0000000..ad2e28c
--- /dev/null
+++ b/plugins/Makefile.am
@@ -0,0 +1,4 @@
+INCLUDES =
+METASOURCES = AUTO
+SUBDIRS = search infowidget ipfilter logviewer upnp partfileimport scheduler \
+ scanfolder rssfeed webinterface zeroconf stats
diff --git a/plugins/infowidget/GeoIP.c b/plugins/infowidget/GeoIP.c
new file mode 100644
index 0000000..f71f830
--- /dev/null
+++ b/plugins/infowidget/GeoIP.c
@@ -0,0 +1,825 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */
+/* GeoIP.c
+ *
+ * Copyright (C) 2003 MaxMind LLC All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "GeoIP.h"
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <netdb.h>
+#include <netinet/in.h> /* For ntohl */
+#include <arpa/inet.h>
+#else
+#include <windows.h>
+#endif
+#include <assert.h>
+#include <sys/types.h> /* for fstat */
+#include <sys/stat.h> /* for fstat */
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h> /* For uint32_t */
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE -1
+#endif
+
+#define COUNTRY_BEGIN 16776960
+#define STATE_BEGIN_REV0 16700000
+#define STATE_BEGIN_REV1 16000000
+#define STRUCTURE_INFO_MAX_SIZE 20
+#define DATABASE_INFO_MAX_SIZE 100
+#define MAX_ORG_RECORD_LENGTH 300
+#define US_OFFSET 1
+#define CANADA_OFFSET 677
+#define WORLD_OFFSET 1353
+#define FIPS_RANGE 360
+
+#define CHECK_ERR(err, msg) { \
+ if (err != Z_OK) { \
+ fprintf(stderr, "%s error: %d\n", msg, err); \
+ exit(1); \
+ } \
+}
+
+const char GeoIP_country_code[247][3] = { "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN","AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","FX","GA","GB","GD","GE","GF","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA","RE","RO","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TM","TN","TO","TP","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","CS","ZA","ZM","ZR","ZW","A1","A2","O1"};
+
+const char GeoIP_country_code3[247][4] = { "--","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT","AGO","AQ","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB","BGD","BEL","BFA","BGR","BHR","BDI","BEN","BMU","BRN","BOL","BRA","BHS","BTN","BV","BWA","BLR","BLZ","CAN","CC","COD","CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI","CUB","CPV","CX","CYP","CZE","DEU","DJI","DNK","DMA","DOM","DZA","ECU","EST","EGY","ESH","ERI","ESP","ETH","FIN","FJI","FLK","FSM","FRO","FRA","FX","GAB","GBR","GRD","GEO","GUF","GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","GS","GTM","GUM","GNB","GUY","HKG","HM","HND","HRV","HTI","HUN","IDN","IRL","ISR","IND","IO","IRQ","IRN","ISL","ITA","JAM","JOR","JPN","KEN","KGZ","KHM","KIR","COM","KNA","PRK","KOR","KWT","CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU","LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI","MMR","MNG","MAC","MNP","MTQ","MRT","MSR","MLT","MUS","MDV","MWI","MEX","MYS","MOZ","NAM","NCL","NER","NFK","NGA","NIC","NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER","PYF","PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW","PRY","QAT","REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN","SWE","SGP","SHN","SVN","SJM","SVK","SLE","SMR","SEN","SOM","SUR","STP","SLV","SYR","SWZ","TCA","TCD","TF","TGO","THA","TJK","TKL","TLS","TKM","TUN","TON","TUR","TTO","TUV","TWN","TZA","UKR","UGA","UM","USA","URY","UZB","VAT","VCT","VEN","VGB","VIR","VNM","VUT","WLF","WSM","YEM","YT","SCG","ZAF","ZMB","ZR","ZWE","A1","A2","O1"};
+
+const char * GeoIP_country_name[247] = {"N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles","Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados","Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia","Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the","Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica","Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic","Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji","Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana","Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala","Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia","Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan","Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis",
+"Korea, Democratic People's Republic of","Korea, Republic of","Kuwait","Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania","Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali","Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives","Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua","Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia","Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau","Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan","Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname","Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand","Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","East Timor","Turkey","Trinidad and Tobago","Tuvalu","Taiwan","Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela","Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia and Montenegro","South Africa","Zambia","Zaire","Zimbabwe",
+"Anonymous Proxy","Satellite Provider","Other"};
+
+const char GeoIP_country_continent[247][3] = {"--","AS","EU","EU","AS","AS","SA","SA","EU","AS","SA","AF","AN","SA","OC","EU","OC","SA","AS","EU","SA","AS","EU","AF","EU","AS","AF","AF","SA","AS","SA","SA","SA","AS","AF","AF","EU","SA","NA","AS","AF","AF","AF","EU","AF","OC","SA","AF","AS","SA","SA","SA","AF","AS","AS","EU","EU","AF","EU","SA","SA","AF","SA","EU","AF","AF","AF","EU","AF","EU","OC","SA","OC","EU","EU","EU","AF","EU","SA","AS","SA","AF","EU","SA","AF","AF","SA","AF","EU","SA","SA","OC","AF","SA","AS","AF","SA","EU","SA","EU","AS","EU","AS","AS","AS","AS","AS","EU","EU","SA","AS","AS","AF","AS","AS","OC","AF","SA","AS","AS","AS","SA","AS","AS","AS","SA","EU","AS","AF","AF","EU","EU","EU","AF","AF","EU","EU","AF","OC","EU","AF","AS","AS","AS","OC","SA","AF","SA","EU","AF","AS","AF","NA","AS","AF","AF","OC","AF","OC","AF","SA","EU","EU","AS","OC","OC","OC","AS","SA","SA","OC","OC","AS","AS","EU","SA","OC","SA","AS","EU","OC","SA","AS","AF","EU","AS","AF","AS","OC","AF","AF","EU","AS","AF","EU","EU","EU","AF","EU","AF","AF","SA","AF","SA","AS","AF","SA","AF","AF","AF","AS","AS","OC","AS","AF","OC","AS","AS","SA","OC","AS","AF","EU","AF","OC","NA","SA","AS","EU","SA","SA","SA","SA","AS","OC","OC","OC","AS","AF","EU","AF","AF","AF","AF"};
+
+const char * GeoIPDBDescription[NUM_DB_TYPES] = {NULL, "GeoIP Country Edition", "GeoIP City Edition, Rev 1", "GeoIP Region Edition, Rev 1", "GeoIP ISP Edition", "GeoIP Organization Edition", "GeoIP City Edition, Rev 0", "GeoIP Region Edition, Rev 0","GeoIP Proxy Edition","GeoIP ASNum Edition","GeoIP Netspeed Edition"};
+
+char *_GeoIP_full_path_to(const char *file_name) {
+ char *path = malloc(sizeof(char) * 1024);
+
+#ifndef _WIN32
+ memset(path, 0, sizeof(char) * 1024);
+ snprintf(path, sizeof(char) * 1024 - 1, "%s/%s", "/home/ivan/geoip.dat", file_name);
+#else
+ char buf[MAX_PATH], *p, *q = NULL;
+ int len;
+ memset(buf, 0, sizeof(buf));
+ len = GetModuleFileName(GetModuleHandle(NULL), buf, sizeof(buf) - 1);
+ for (p = buf + len; p > buf; p--)
+ if (*p == '\\')
+ {
+ if (!q)
+ q = p;
+ else
+ *p = '/';
+ }
+ *q = 0;
+ memset(path, 0, sizeof(char) * 1024);
+ snprintf(path, sizeof(char) * 1024 - 1, "%s/%s", buf, file_name);
+#endif
+
+ return path;
+}
+
+char ** GeoIPDBFileName = NULL;
+
+void _GeoIP_setup_dbfilename() {
+ if (NULL == GeoIPDBFileName) {
+ GeoIPDBFileName = malloc(sizeof(char *) * NUM_DB_TYPES);
+ memset(GeoIPDBFileName, 0, sizeof(char *) * NUM_DB_TYPES);
+
+ GeoIPDBFileName[GEOIP_COUNTRY_EDITION] = _GeoIP_full_path_to("GeoIP.dat");
+ GeoIPDBFileName[GEOIP_REGION_EDITION_REV0] = _GeoIP_full_path_to("GeoIPRegion.dat");
+ GeoIPDBFileName[GEOIP_REGION_EDITION_REV1] = _GeoIP_full_path_to("GeoIPRegion.dat");
+ GeoIPDBFileName[GEOIP_CITY_EDITION_REV0] = _GeoIP_full_path_to("GeoIPCity.dat");
+ GeoIPDBFileName[GEOIP_CITY_EDITION_REV1] = _GeoIP_full_path_to("GeoIPCity.dat");
+ GeoIPDBFileName[GEOIP_ISP_EDITION] = _GeoIP_full_path_to("GeoIPISP.dat");
+ GeoIPDBFileName[GEOIP_ORG_EDITION] = _GeoIP_full_path_to("GeoIPOrg.dat");
+ GeoIPDBFileName[GEOIP_PROXY_EDITION] = _GeoIP_full_path_to("GeoIPProxy.dat");
+ GeoIPDBFileName[GEOIP_ASNUM_EDITION] = _GeoIP_full_path_to("GeoIPASNum.dat");
+ GeoIPDBFileName[GEOIP_NETSPEED_EDITION] = _GeoIP_full_path_to("GeoIPNetSpeed.dat");
+ }
+}
+
+static
+int _file_exists(const char *file_name) {
+ struct stat file_stat;
+ return( (stat(file_name, &file_stat) == 0) ? 1:0);
+}
+
+int GeoIP_db_avail(int type) {
+ const char * filePath;
+ if (type < 0 || type >= NUM_DB_TYPES) {
+ return 0;
+ }
+ filePath = GeoIPDBFileName[type];
+ if (NULL == filePath) {
+ return 0;
+ }
+ return _file_exists(filePath);
+}
+
+static
+void _setup_segments(GeoIP * gi) {
+ int i, j;
+ unsigned char delim[3];
+ unsigned char buf[SEGMENT_RECORD_LENGTH];
+
+ /* default to GeoIP Country Edition */
+ gi->databaseType = GEOIP_COUNTRY_EDITION;
+ gi->record_length = STANDARD_RECORD_LENGTH;
+ fseek(gi->GeoIPDatabase, -3l, SEEK_END);
+ for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
+ fread(delim, 1, 3, gi->GeoIPDatabase);
+ if (delim[0] == 255 && delim[1] == 255 && delim[2] == 255) {
+ fread(&gi->databaseType, 1, 1, gi->GeoIPDatabase);
+ if (gi->databaseType >= 106) {
+ /* backwards compatibility with databases from April 2003 and earlier */
+ gi->databaseType -= 105;
+ }
+
+ if (gi->databaseType == GEOIP_REGION_EDITION_REV0) {
+ /* Region Edition, pre June 2003 */
+ gi->databaseSegments = malloc(sizeof(int));
+ gi->databaseSegments[0] = STATE_BEGIN_REV0;
+ } else if (gi->databaseType == GEOIP_REGION_EDITION_REV1) {
+ /* Region Edition, post June 2003 */
+ gi->databaseSegments = malloc(sizeof(int));
+ gi->databaseSegments[0] = STATE_BEGIN_REV1;
+ } else if (gi->databaseType == GEOIP_CITY_EDITION_REV0 ||
+ gi->databaseType == GEOIP_CITY_EDITION_REV1 ||
+ gi->databaseType == GEOIP_ORG_EDITION ||
+ gi->databaseType == GEOIP_ISP_EDITION ||
+ gi->databaseType == GEOIP_ASNUM_EDITION) {
+ /* City/Org Editions have two segments, read offset of second segment */
+ gi->databaseSegments = malloc(sizeof(int));
+ gi->databaseSegments[0] = 0;
+ fread(buf, SEGMENT_RECORD_LENGTH, 1, gi->GeoIPDatabase);
+ for (j = 0; j < SEGMENT_RECORD_LENGTH; j++) {
+ gi->databaseSegments[0] += (buf[j] << (j * 8));
+ }
+ if (gi->databaseType == GEOIP_ORG_EDITION ||
+ gi->databaseType == GEOIP_ISP_EDITION)
+ gi->record_length = ORG_RECORD_LENGTH;
+ }
+ break;
+ } else {
+ fseek(gi->GeoIPDatabase, -4l, SEEK_CUR);
+ }
+ }
+ if (gi->databaseType == GEOIP_COUNTRY_EDITION ||
+ gi->databaseType == GEOIP_PROXY_EDITION ||
+ gi->databaseType == GEOIP_NETSPEED_EDITION) {
+ gi->databaseSegments = malloc(sizeof(int));
+ gi->databaseSegments[0] = COUNTRY_BEGIN;
+ }
+}
+
+static
+int _check_mtime(GeoIP *gi) {
+ struct stat buf;
+
+ if (gi->flags & GEOIP_CHECK_CACHE) {
+ if (fstat(fileno(gi->GeoIPDatabase), &buf) != -1) {
+ if (buf.st_mtime > gi->mtime) {
+ /* GeoIP Database file updated */
+ if (gi->flags & GEOIP_MEMORY_CACHE) {
+ /* reload database into memory cache */
+ if (realloc(gi->cache, buf.st_size) != NULL) {
+ if (fread(gi->cache, sizeof(unsigned char), buf.st_size, gi->GeoIPDatabase) != (size_t) buf.st_size) {
+ fprintf(stderr,"Error reading file %s\n",gi->file_path);
+ return -1;
+ }
+ gi->mtime = buf.st_mtime;
+ }
+ } else {
+ /* refresh filehandle */
+ fclose(gi->GeoIPDatabase);
+ if (gi->databaseSegments != NULL)
+ free(gi->databaseSegments);
+ gi->GeoIPDatabase = fopen(gi->file_path,"rb");
+ if (gi->GeoIPDatabase == NULL) {
+ fprintf(stderr,"Error Opening file %s\n",gi->file_path);
+ return -1;
+ }
+ _setup_segments(gi);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+unsigned int _GeoIP_seek_record (GeoIP *gi, unsigned long ipnum) {
+ int depth;
+ unsigned int x;
+ unsigned char stack_buffer[2 * MAX_RECORD_LENGTH];
+ const unsigned char *buf = (gi->cache == NULL) ? stack_buffer : NULL;
+ unsigned int offset = 0;
+
+ const unsigned char * p;
+ int j;
+
+ _check_mtime(gi);
+ for (depth = 31; depth >= 0; depth--) {
+ if (gi->cache == NULL && gi->index_cache == NULL) {
+ /* read from disk */
+ fseek(gi->GeoIPDatabase, (long)gi->record_length * 2 * offset, SEEK_SET);
+ fread(stack_buffer,gi->record_length,2,gi->GeoIPDatabase);
+ } else if (gi->index_cache == NULL) {
+ /* simply point to record in memory */
+ buf = gi->cache + (long)gi->record_length * 2 *offset;
+ } else {
+ buf = gi->index_cache + (long)gi->record_length * 2 * offset;
+ }
+
+ if (ipnum & (1 << depth)) {
+ /* Take the right-hand branch */
+ if ( gi->record_length == 3 ) {
+ /* Most common case is completely unrolled and uses constants. */
+ x = (buf[3*1 + 0] << (0*8))
+ + (buf[3*1 + 1] << (1*8))
+ + (buf[3*1 + 2] << (2*8));
+
+ } else {
+ /* General case */
+ j = gi->record_length;
+ p = &buf[2*j];
+ x = 0;
+ do {
+ x <<= 8;
+ x += *(--p);
+ } while ( --j );
+ }
+
+ } else {
+ /* Take the left-hand branch */
+ if ( gi->record_length == 3 ) {
+ /* Most common case is completely unrolled and uses constants. */
+ x = (buf[3*0 + 0] << (0*8))
+ + (buf[3*0 + 1] << (1*8))
+ + (buf[3*0 + 2] << (2*8));
+ } else {
+ /* General case */
+ j = gi->record_length;
+ p = &buf[1*j];
+ x = 0;
+ do {
+ x <<= 8;
+ x += *(--p);
+ } while ( --j );
+ }
+ }
+
+ if (x >= gi->databaseSegments[0]) {
+ return x;
+ }
+ offset = x;
+ }
+
+ /* shouldn't reach here */
+ fprintf(stderr,"Error Traversing Database for ipnum = %lu - Perhaps database is corrupt?\n",ipnum);
+ return 0;
+}
+
+unsigned long _GeoIP_addr_to_num (const char *addr) {
+ int i;
+ char tok[4];
+ int octet;
+ int j = 0, k = 0;
+ unsigned long ipnum = 0;
+ char c = 0;
+
+ for (i=0; i<4; i++) {
+ for (;;) {
+ c = addr[k++];
+ if (c == '.' || c == '\0') {
+ tok[j] = '\0';
+ octet = atoi(tok);
+ if (octet > 255)
+ return 0;
+ ipnum += (octet << ((3-i)*8));
+ j = 0;
+ break;
+ } else if (c >= '0' && c<= '9') {
+ if (j > 2) {
+ return 0;
+ }
+ tok[j++] = c;
+ } else {
+ return 0;
+ }
+ }
+ if(c == '\0' && i<3) {
+ return 0;
+ }
+ }
+ return ipnum;
+}
+
+GeoIP* GeoIP_open_type (int type, int flags) {
+ GeoIP * gi;
+ const char * filePath;
+ if (type < 0 || type >= NUM_DB_TYPES) {
+ printf("Invalid database type %d\n", type);
+ return NULL;
+ }
+ _GeoIP_setup_dbfilename();
+ filePath = GeoIPDBFileName[type];
+ if (filePath == NULL) {
+ printf("Invalid database type %d\n", type);
+ return NULL;
+ }
+ gi = GeoIP_open (filePath, flags);
+ return gi;
+}
+
+GeoIP* GeoIP_new (int flags) {
+ GeoIP * gi;
+ _GeoIP_setup_dbfilename();
+ gi = GeoIP_open (GeoIPDBFileName[GEOIP_COUNTRY_EDITION], flags);
+ return gi;
+}
+
+GeoIP* GeoIP_open (const char * filename, int flags) {
+ struct stat buf;
+#ifdef _WIN32
+ WSADATA wsa;
+ if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0)
+ return NULL;
+#endif
+
+ GeoIP *gi = (GeoIP *)malloc(sizeof(GeoIP));
+ if (gi == NULL)
+ return NULL;
+ gi->file_path = malloc(sizeof(char) * (strlen(filename)+1));
+ if (gi->file_path == NULL)
+ return NULL;
+ strcpy(gi->file_path, filename);
+ gi->GeoIPDatabase = fopen(filename,"rb");
+ if (gi->GeoIPDatabase == NULL) {
+ fprintf(stderr,"Error Opening file %s\n",filename);
+ free(gi->file_path);
+ free(gi);
+ return NULL;
+ } else {
+ if (flags & GEOIP_MEMORY_CACHE) {
+ if (fstat(fileno(gi->GeoIPDatabase), &buf) == -1) {
+ fprintf(stderr,"Error stating file %s\n",filename);
+ free(gi);
+ return NULL;
+ }
+ gi->mtime = buf.st_mtime;
+ gi->cache = (unsigned char *) malloc(sizeof(unsigned char) * buf.st_size);
+ if (gi->cache != NULL) {
+ if (fread(gi->cache, sizeof(unsigned char), buf.st_size, gi->GeoIPDatabase) != (size_t) buf.st_size) {
+ fprintf(stderr,"Error reading file %s\n",filename);
+ free(gi->cache);
+ free(gi);
+ return NULL;
+ }
+ }
+ } else {
+ if (flags & GEOIP_CHECK_CACHE) {
+ if (fstat(fileno(gi->GeoIPDatabase), &buf) == -1) {
+ fprintf(stderr,"Error stating file %s\n",filename);
+ free(gi);
+ return NULL;
+ }
+ gi->mtime = buf.st_mtime;
+ }
+ gi->cache = NULL;
+ }
+ gi->flags = flags;
+ _setup_segments(gi);
+ if (flags & GEOIP_INDEX_CACHE) {
+ gi->index_cache = (unsigned char *) malloc(sizeof(unsigned char) * ((gi->databaseSegments[0] * (long)gi->record_length * 2)));
+ if (gi->index_cache != NULL) {
+ fseek(gi->GeoIPDatabase, 0, SEEK_SET);
+ if (fread(gi->index_cache, sizeof(unsigned char), gi->databaseSegments[0] * (long)gi->record_length * 2, gi->GeoIPDatabase) != (size_t) (gi->databaseSegments[0]*(long)gi->record_length * 2)) {
+ fprintf(stderr,"Error reading file %s\n",filename);
+ free(gi->index_cache);
+ free(gi);
+ return NULL;
+ }
+ }
+ } else {
+ gi->index_cache = NULL;
+ }
+ return gi;
+ }
+}
+
+void GeoIP_delete (GeoIP *gi) {
+ if (gi->GeoIPDatabase != NULL)
+ fclose(gi->GeoIPDatabase);
+ if (gi->cache != NULL)
+ free(gi->cache);
+ if (gi->index_cache != NULL)
+ free(gi->index_cache);
+ if (gi->file_path != NULL)
+ free(gi->file_path);
+ if (gi->databaseSegments != NULL)
+ free(gi->databaseSegments);
+ free(gi);
+}
+
+const char *GeoIP_country_code_by_name (GeoIP* gi, const char *name) {
+ int country_id;
+ country_id = GeoIP_id_by_name(gi, name);
+ return (country_id > 0) ? GeoIP_country_code[country_id] : NULL;
+}
+
+const char *GeoIP_country_code3_by_name (GeoIP* gi, const char *name) {
+ int country_id;
+ country_id = GeoIP_id_by_name(gi, name);
+ return (country_id > 0) ? GeoIP_country_code3[country_id] : NULL;
+}
+
+const char *GeoIP_country_name_by_name (GeoIP* gi, const char *name) {
+ int country_id;
+ country_id = GeoIP_id_by_name(gi, name);
+ return (country_id > 0) ? GeoIP_country_name[country_id] : NULL;
+}
+
+unsigned long _GeoIP_lookupaddress (const char *host) {
+ unsigned long addr = inet_addr(host);
+ struct hostent phe2;
+ struct hostent * phe = &phe2;
+ char *buf = NULL;
+ int buflength = 16384;
+ int herr = 0;
+ int result = 0;
+#ifdef HAVE_GETHOSTBYNAME_R
+ buf = malloc(buflength);
+#endif
+ if (addr == INADDR_NONE) {
+#ifdef HAVE_GETHOSTBYNAME_R
+ while (1) {
+ /* we use gethostbyname_r here because it is thread-safe and gethostbyname is not */
+#ifdef GETHOSTBYNAME_R_RETURNS_INT
+ result = gethostbyname_r(host,&phe2,buf,buflength,&phe,&herr);
+#else
+ phe = gethostbyname_r(host,&phe2,buf,buflength,&herr);
+#endif
+ if (herr != ERANGE)
+ break;
+ if (result == 0)
+ break;
+ /* double the buffer if the buffer is too small */
+ buflength = buflength * 2;
+ buf = realloc(buf,buflength);
+ }
+#endif
+#ifndef HAVE_GETHOSTBYNAME_R
+ /* Some systems do not support gethostbyname_r, such as Mac OS X */
+ phe = gethostbyname(host);
+#endif
+ if (!phe || result != 0) {
+ free(buf);
+ return 0;
+ }
+ addr = *((unsigned long *) phe->h_addr_list[0]);
+ }
+#ifdef HAVE_GETHOSTBYNAME_R
+ free(buf);
+#endif
+ return ntohl(addr);
+}
+
+int GeoIP_id_by_name (GeoIP* gi, const char *name) {
+ unsigned long ipnum;
+ int ret;
+ if (name == NULL) {
+ return 0;
+ }
+ if (gi->databaseType != GEOIP_COUNTRY_EDITION && gi->databaseType != GEOIP_PROXY_EDITION && gi->databaseType != GEOIP_NETSPEED_EDITION) {
+ printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_COUNTRY_EDITION]);
+ return 0;
+ }
+ if (!(ipnum = _GeoIP_lookupaddress(name)))
+ return 0;
+ ret = _GeoIP_seek_record(gi, ipnum) - COUNTRY_BEGIN;
+ return ret;
+
+}
+
+const char *GeoIP_country_code_by_addr (GeoIP* gi, const char *addr) {
+ int country_id;
+ country_id = GeoIP_id_by_addr(gi, addr);
+ return (country_id > 0) ? GeoIP_country_code[country_id] : NULL;
+}
+
+const char *GeoIP_country_code3_by_addr (GeoIP* gi, const char *addr) {
+ int country_id;
+ country_id = GeoIP_id_by_addr(gi, addr);
+ return (country_id > 0) ? GeoIP_country_code3[country_id] : NULL;
+ return GeoIP_country_code3[country_id];
+}
+
+const char *GeoIP_country_name_by_addr (GeoIP* gi, const char *addr) {
+ int country_id;
+ country_id = GeoIP_id_by_addr(gi, addr);
+ return (country_id > 0) ? GeoIP_country_name[country_id] : NULL;
+ return GeoIP_country_name[country_id];
+}
+
+const char *GeoIP_country_name_by_ipnum (GeoIP* gi, unsigned long ipnum) {
+ int country_id;
+ country_id = GeoIP_id_by_ipnum(gi, ipnum);
+ return (country_id > 0) ? GeoIP_country_name[country_id] : NULL;
+}
+
+const char *GeoIP_country_code_by_ipnum (GeoIP* gi, unsigned long ipnum) {
+ int country_id;
+ country_id = GeoIP_id_by_ipnum(gi, ipnum);
+ return (country_id > 0) ? GeoIP_country_code[country_id] : NULL;
+}
+
+const char *GeoIP_country_code3_by_ipnum (GeoIP* gi, unsigned long ipnum) {
+ int country_id;
+ country_id = GeoIP_id_by_ipnum(gi, ipnum);
+ return (country_id > 0) ? GeoIP_country_code3[country_id] : NULL;
+}
+
+int GeoIP_country_id_by_addr (GeoIP* gi, const char *addr) {
+ return GeoIP_id_by_addr(gi, addr);
+}
+
+int GeoIP_country_id_by_name (GeoIP* gi, const char *host) {
+ return GeoIP_id_by_name(gi, host);
+}
+
+int GeoIP_id_by_addr (GeoIP* gi, const char *addr) {
+ unsigned long ipnum;
+ int ret;
+ if (addr == NULL) {
+ return 0;
+ }
+ if (gi->databaseType != GEOIP_COUNTRY_EDITION &&
+ gi->databaseType != GEOIP_PROXY_EDITION &&
+ gi->databaseType != GEOIP_NETSPEED_EDITION) {
+ printf("Invalid database type %s, expected %s\n",
+ GeoIPDBDescription[(int)gi->databaseType],
+ GeoIPDBDescription[GEOIP_COUNTRY_EDITION]);
+ return 0;
+ }
+ ipnum = _GeoIP_addr_to_num(addr);
+ ret = _GeoIP_seek_record(gi, ipnum) - COUNTRY_BEGIN;
+ return ret;
+}
+
+int GeoIP_id_by_ipnum (GeoIP* gi, unsigned long ipnum) {
+ int ret;
+ if (ipnum == 0) {
+ return 0;
+ }
+ if (gi->databaseType != GEOIP_COUNTRY_EDITION &&
+ gi->databaseType != GEOIP_PROXY_EDITION &&
+ gi->databaseType != GEOIP_NETSPEED_EDITION) {
+ printf("Invalid database type %s, expected %s\n",
+ GeoIPDBDescription[(int)gi->databaseType],
+ GeoIPDBDescription[GEOIP_COUNTRY_EDITION]);
+ return 0;
+ }
+ ret = _GeoIP_seek_record(gi, ipnum) - COUNTRY_BEGIN;
+ return ret;
+}
+
+char *GeoIP_database_info (GeoIP* gi) {
+ int i;
+ unsigned char buf[3];
+ char *retval;
+ int hasStructureInfo = 0;
+
+ if(gi == NULL)
+ return NULL;
+
+ _check_mtime(gi);
+ fseek(gi->GeoIPDatabase, -3l, SEEK_END);
+
+ /* first get past the database structure information */
+ for (i = 0; i < STRUCTURE_INFO_MAX_SIZE; i++) {
+ fread(buf, 1, 3, gi->GeoIPDatabase);
+ if (buf[0] == 255 && buf[1] == 255 && buf[2] == 255) {
+ hasStructureInfo = 1;
+ break;
+ }
+ fseek(gi->GeoIPDatabase, -4l, SEEK_CUR);
+ }
+ if (hasStructureInfo == 1) {
+ fseek(gi->GeoIPDatabase, -3l, SEEK_CUR);
+ } else {
+ /* no structure info, must be pre Sep 2002 database, go back to end */
+ fseek(gi->GeoIPDatabase, -3l, SEEK_END);
+ }
+
+ for (i = 0; i < DATABASE_INFO_MAX_SIZE; i++) {
+ fread(buf, 1, 3, gi->GeoIPDatabase);
+ if (buf[0] == 0 && buf[1] == 0 && buf[2] == 0) {
+ retval = malloc(sizeof(char) * (i+1));
+ if (retval == NULL) {
+ return NULL;
+ }
+ fread(retval, 1, i, gi->GeoIPDatabase);
+ retval[i] = '\0';
+ return retval;
+ }
+ fseek(gi->GeoIPDatabase, -4l, SEEK_CUR);
+ }
+ return NULL;
+}
+
+/* GeoIP Region Edition functions */
+
+void GeoIP_assign_region_by_inetaddr(GeoIP* gi, unsigned long inetaddr, GeoIPRegion *region) {
+ unsigned int seek_region;
+
+ /* This also writes in the terminating NULs (if you decide to
+ * keep them) and clear any fields that are not set. */
+ memset(region, 0, sizeof(GeoIPRegion));
+
+ seek_region = _GeoIP_seek_record(gi, ntohl(inetaddr));
+
+ if (gi->databaseType == GEOIP_REGION_EDITION_REV0) {
+ /* Region Edition, pre June 2003 */
+ seek_region -= STATE_BEGIN_REV0;
+ if (seek_region >= 1000) {
+ region->country_code[0] = 'U';
+ region->country_code[1] = 'S';
+ region->region[0] = (char) ((seek_region - 1000)/26 + 65);
+ region->region[1] = (char) ((seek_region - 1000)%26 + 65);
+ } else {
+ memcpy(region->country_code, GeoIP_country_code[seek_region], 2);
+ }
+ } else if (gi->databaseType == GEOIP_REGION_EDITION_REV1) {
+ /* Region Edition, post June 2003 */
+ seek_region -= STATE_BEGIN_REV1;
+ if (seek_region < US_OFFSET) {
+ /* Unknown */
+ /* we don't need to do anything here b/c we memset region to 0 */
+ } else if (seek_region < CANADA_OFFSET) {
+ /* USA State */
+ region->country_code[0] = 'U';
+ region->country_code[1] = 'S';
+ region->region[0] = (char) ((seek_region - US_OFFSET)/26 + 65);
+ region->region[1] = (char) ((seek_region - US_OFFSET)%26 + 65);
+ } else if (seek_region < WORLD_OFFSET) {
+ /* Canada Province */
+ region->country_code[0] = 'C';
+ region->country_code[1] = 'A';
+ region->region[0] = (char) ((seek_region - CANADA_OFFSET)/26 + 65);
+ region->region[1] = (char) ((seek_region - CANADA_OFFSET)%26 + 65);
+ } else {
+ /* Not US or Canada */
+ memcpy(region->country_code, GeoIP_country_code[(seek_region - WORLD_OFFSET) / FIPS_RANGE], 2);
+ }
+ }
+}
+
+static
+GeoIPRegion * _get_region(GeoIP* gi, unsigned long ipnum) {
+ GeoIPRegion * region;
+
+ region = malloc(sizeof(GeoIPRegion));
+ if (region) {
+ GeoIP_assign_region_by_inetaddr(gi, htonl(ipnum), region);
+ }
+ return region;
+}
+
+GeoIPRegion * GeoIP_region_by_addr (GeoIP* gi, const char *addr) {
+ unsigned long ipnum;
+ if (addr == NULL) {
+ return 0;
+ }
+ if (gi->databaseType != GEOIP_REGION_EDITION_REV0 &&
+ gi->databaseType != GEOIP_REGION_EDITION_REV1) {
+ printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_REGION_EDITION_REV1]);
+ return 0;
+ }
+ ipnum = _GeoIP_addr_to_num(addr);
+ return _get_region(gi, ipnum);
+}
+
+GeoIPRegion * GeoIP_region_by_name (GeoIP* gi, const char *name) {
+ unsigned long ipnum;
+ if (name == NULL) {
+ return 0;
+ }
+ if (gi->databaseType != GEOIP_REGION_EDITION_REV0 &&
+ gi->databaseType != GEOIP_REGION_EDITION_REV1) {
+ printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_REGION_EDITION_REV1]);
+ return 0;
+ }
+ if (!(ipnum = _GeoIP_lookupaddress(name)))
+ return 0;
+ return _get_region(gi, ipnum);
+}
+
+GeoIPRegion * GeoIP_region_by_ipnum (GeoIP* gi, unsigned long ipnum) {
+ if (gi->databaseType != GEOIP_REGION_EDITION_REV0 &&
+ gi->databaseType != GEOIP_REGION_EDITION_REV1) {
+ printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_REGION_EDITION_REV1]);
+ return 0;
+ }
+ return _get_region(gi, ipnum);
+}
+
+void GeoIPRegion_delete (GeoIPRegion *gir) {
+ free(gir);
+}
+
+/* GeoIP Organization, ISP and AS Number Edition private method */
+static
+char *_get_name (GeoIP* gi, unsigned long ipnum) {
+ int seek_org;
+ char buf[MAX_ORG_RECORD_LENGTH];
+ char * org_buf, * buf_pointer;
+ int record_pointer;
+
+ if (gi->databaseType != GEOIP_ORG_EDITION &&
+ gi->databaseType != GEOIP_ISP_EDITION &&
+ gi->databaseType != GEOIP_ASNUM_EDITION) {
+ printf("Invalid database type %s, expected %s\n", GeoIPDBDescription[(int)gi->databaseType], GeoIPDBDescription[GEOIP_ORG_EDITION]);
+ return 0;
+ }
+
+ seek_org = _GeoIP_seek_record(gi, ipnum);
+ if (seek_org == gi->databaseSegments[0])
+ return NULL;
+
+ record_pointer = seek_org + (2 * gi->record_length - 1) * gi->databaseSegments[0];
+
+ if (gi->cache == NULL) {
+ fseek(gi->GeoIPDatabase, record_pointer, SEEK_SET);
+ fread(buf, sizeof(char), MAX_ORG_RECORD_LENGTH, gi->GeoIPDatabase);
+ org_buf = malloc(sizeof(char) * (strlen(buf)+1));
+ strcpy(org_buf, buf);
+ } else {
+ buf_pointer = gi->cache + (long)record_pointer;
+ org_buf = malloc(sizeof(char) * (strlen(buf_pointer)+1));
+ strcpy(org_buf, buf_pointer);
+ }
+ return org_buf;
+}
+
+char *GeoIP_name_by_ipnum (GeoIP* gi, unsigned long ipnum) {
+ return _get_name(gi,ipnum);
+}
+
+char *GeoIP_name_by_addr (GeoIP* gi, const char *addr) {
+ unsigned long ipnum;
+ if (addr == NULL) {
+ return 0;
+ }
+ ipnum = _GeoIP_addr_to_num(addr);
+ return _get_name(gi, ipnum);
+}
+
+char *GeoIP_name_by_name (GeoIP* gi, const char *name) {
+ unsigned long ipnum;
+ if (name == NULL) {
+ return 0;
+ }
+ if (!(ipnum = _GeoIP_lookupaddress(name)))
+ return 0;
+ return _get_name(gi, ipnum);
+}
+
+char *GeoIP_org_by_ipnum (GeoIP* gi, unsigned long ipnum) {
+ return GeoIP_name_by_ipnum(gi, ipnum);
+}
+
+char *GeoIP_org_by_addr (GeoIP* gi, const char *addr) {
+ return GeoIP_name_by_addr(gi, addr);
+}
+
+char *GeoIP_org_by_name (GeoIP* gi, const char *name) {
+ return GeoIP_name_by_name(gi, name);
+}
+
+unsigned char GeoIP_database_edition (GeoIP* gi) {
+ return gi->databaseType;
+}
diff --git a/plugins/infowidget/GeoIP.h b/plugins/infowidget/GeoIP.h
new file mode 100644
index 0000000..1403e4d
--- /dev/null
+++ b/plugins/infowidget/GeoIP.h
@@ -0,0 +1,161 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */
+/* GeoIP.h
+ *
+ * Copyright (C) 2003 MaxMind LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef GEOIP_H
+#define GEOIP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+#include <sys/types.h> /* for fstat */
+#include <sys/stat.h> /* for fstat */
+
+#define SEGMENT_RECORD_LENGTH 3
+#define STANDARD_RECORD_LENGTH 3
+#define ORG_RECORD_LENGTH 4
+#define MAX_RECORD_LENGTH 4
+#define NUM_DB_TYPES 16
+
+typedef struct GeoIPTag {
+ FILE *GeoIPDatabase;
+ char *file_path;
+ unsigned char *cache;
+ unsigned char *index_cache;
+ unsigned int *databaseSegments;
+ char databaseType;
+ time_t mtime;
+ int flags;
+ char record_length;
+ int record_iter; /* used in GeoIP_next_record */
+} GeoIP;
+
+typedef struct GeoIPRegionTag {
+ char country_code[3];
+ char region[3];
+} GeoIPRegion;
+
+typedef enum {
+ GEOIP_STANDARD = 0,
+ GEOIP_MEMORY_CACHE = 1,
+ GEOIP_CHECK_CACHE = 2,
+ GEOIP_INDEX_CACHE = 4
+} GeoIPOptions;
+
+typedef enum {
+ GEOIP_COUNTRY_EDITION = 1,
+ GEOIP_REGION_EDITION_REV0 = 7,
+ GEOIP_CITY_EDITION_REV0 = 6,
+ GEOIP_ORG_EDITION = 5,
+ GEOIP_ISP_EDITION = 4,
+ GEOIP_CITY_EDITION_REV1 = 2,
+ GEOIP_REGION_EDITION_REV1 = 3,
+ GEOIP_PROXY_EDITION = 8,
+ GEOIP_ASNUM_EDITION = 9,
+ GEOIP_NETSPEED_EDITION = 10
+} GeoIPDBTypes;
+
+typedef enum {
+ GEOIP_ANON_PROXY = 1,
+ GEOIP_HTTP_X_FORWARDED_FOR_PROXY = 2,
+ GEOIP_HTTP_CLIENT_IP_PROXY = 3
+} GeoIPProxyTypes;
+
+typedef enum {
+ GEOIP_UNKNOWN_SPEED = 0,
+ GEOIP_DIALUP_SPEED = 1,
+ GEOIP_CABLEDSL_SPEED = 2,
+ GEOIP_CORPORATE_SPEED = 3
+} GeoIPNetspeedValues;
+
+extern char **GeoIPDBFileName;
+extern const char * GeoIPDBDescription[NUM_DB_TYPES];
+extern const char *GeoIPCountryDBFileName;
+extern const char *GeoIPRegionDBFileName;
+extern const char *GeoIPCityDBFileName;
+extern const char *GeoIPOrgDBFileName;
+extern const char *GeoIPISPDBFileName;
+
+extern const char GeoIP_country_code[247][3];
+extern const char GeoIP_country_code3[247][4];
+extern const char * GeoIP_country_name[247];
+extern const char GeoIP_country_continent[247][3];
+
+#ifdef DLL
+#define GEOIP_API __declspec(dllexport)
+#else
+#define GEOIP_API
+#endif /* DLL */
+
+GEOIP_API GeoIP* GeoIP_open_type (int type, int flags);
+GEOIP_API GeoIP* GeoIP_new(int flags);
+GEOIP_API GeoIP* GeoIP_open(const char * filename, int flags);
+GEOIP_API int GeoIP_db_avail(int type);
+GEOIP_API void GeoIP_delete(GeoIP* gi);
+GEOIP_API const char *GeoIP_country_code_by_addr (GeoIP* gi, const char *addr);
+GEOIP_API const char *GeoIP_country_code_by_name (GeoIP* gi, const char *host);
+GEOIP_API const char *GeoIP_country_code3_by_addr (GeoIP* gi, const char *addr);
+GEOIP_API const char *GeoIP_country_code3_by_name (GeoIP* gi, const char *host);
+GEOIP_API const char *GeoIP_country_name_by_addr (GeoIP* gi, const char *addr);
+GEOIP_API const char *GeoIP_country_name_by_name (GeoIP* gi, const char *host);
+GEOIP_API const char *GeoIP_country_name_by_ipnum (GeoIP* gi, unsigned long ipnum);
+GEOIP_API const char *GeoIP_country_code_by_ipnum (GeoIP* gi, unsigned long ipnum);
+GEOIP_API const char *GeoIP_country_code3_by_ipnum (GeoIP* gi, unsigned long ipnum);
+
+/* Deprecated - for backwards compatibility only */
+GEOIP_API int GeoIP_country_id_by_addr (GeoIP* gi, const char *addr);
+GEOIP_API int GeoIP_country_id_by_name (GeoIP* gi, const char *host);
+GEOIP_API char *GeoIP_org_by_addr (GeoIP* gi, const char *addr);
+GEOIP_API char *GeoIP_org_by_name (GeoIP* gi, const char *host);
+/* End deprecated */
+
+GEOIP_API int GeoIP_id_by_addr (GeoIP* gi, const char *addr);
+GEOIP_API int GeoIP_id_by_name (GeoIP* gi, const char *host);
+GEOIP_API int GeoIP_id_by_ipnum (GeoIP* gi, unsigned long ipnum);
+
+GEOIP_API GeoIPRegion * GeoIP_region_by_addr (GeoIP* gi, const char *addr);
+GEOIP_API GeoIPRegion * GeoIP_region_by_name (GeoIP* gi, const char *host);
+GEOIP_API GeoIPRegion * GeoIP_region_by_ipnum (GeoIP *gi, unsigned long ipnum);
+
+/* Warning - don't call this after GeoIP_assign_region_by_inetaddr calls */
+GEOIP_API void GeoIPRegion_delete (GeoIPRegion *gir);
+
+GEOIP_API void GeoIP_assign_region_by_inetaddr(GeoIP* gi, unsigned long inetaddr, GeoIPRegion *gir);
+
+/* Used to query GeoIP Organization, ISP and AS Number databases */
+GEOIP_API char *GeoIP_name_by_ipnum (GeoIP* gi, unsigned long ipnum);
+GEOIP_API char *GeoIP_name_by_addr (GeoIP* gi, const char *addr);
+GEOIP_API char *GeoIP_name_by_name (GeoIP* gi, const char *host);
+
+GEOIP_API char *GeoIP_database_info (GeoIP* gi);
+GEOIP_API unsigned char GeoIP_database_edition (GeoIP* gi);
+
+#ifdef BSD
+#define memcpy(dest, src, n) bcopy(src, dest, n)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GEOIP_H */
diff --git a/plugins/infowidget/Makefile.am b/plugins/infowidget/Makefile.am
new file mode 100644
index 0000000..66a5820
--- /dev/null
+++ b/plugins/infowidget/Makefile.am
@@ -0,0 +1,40 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+kde_module_LTLIBRARIES = ktinfowidgetplugin.la
+noinst_HEADERS = infowidgetplugin.h infowidgetprefpage.h trackerview.h GeoIP.h \
+ statustab.h fileview.h
+ktinfowidgetplugin_la_SOURCES = infowidgetplugin.cpp availabilitychunkbar.cpp \
+ chunkbar.cpp chunkdownloadview.cpp downloadedchunkbar.cpp flagdb.cpp peerview.cpp \
+ ktorrentmonitor.cpp iwfiletreediritem.cpp iwfiletreeitem.cpp infowidgetprefpage.cpp \
+ infowidgetpluginsettings.kcfgc iwpref.ui trackerviewbase.ui trackerview.cpp floatspinbox.cpp \
+ localefloatvalidator.cpp chunkdownloadviewbase.ui statustabbase.ui statustab.cpp fileview.cpp
+
+# Libs needed by the plugin
+ktinfowidgetplugin_la_LIBADD = ../../libktorrent/libktorrent.la \
+ $(LIB_KHTML) $(LIB_KPARTS) $(LIB_QT) \
+ $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KFILE)
+
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktinfowidgetplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+if USE_SYSTEM_GEOIP
+ktinfowidgetplugin_la_LDFLAGS += -lGeoIP
+else
+ktinfowidgetplugin_la_SOURCES += GeoIP.c
+endif
+
+# rc file containing the GUI for the plugin
+# pluginsdir = $(kde_datadir)/ktsearchplugin
+# plugins_DATA = ktsearchpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+kde_services_DATA = ktinfowidgetplugin.desktop
+
+kde_kcfg_DATA = ktinfowidgetplugin.kcfg
+
+ktdatadir = $(kde_datadir)/ktorrent/geoip
+
+SUBDIRS = geoip
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/infowidget/availabilitychunkbar.cpp b/plugins/infowidget/availabilitychunkbar.cpp
new file mode 100644
index 0000000..9d96268
--- /dev/null
+++ b/plugins/infowidget/availabilitychunkbar.cpp
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qtooltip.h>
+#include <klocale.h>
+
+#include <util/bitset.h>
+#include <interfaces/torrentinterface.h>
+#include "availabilitychunkbar.h"
+
+namespace kt
+{
+
+AvailabilityChunkBar::AvailabilityChunkBar(QWidget* parent, const char* name): ChunkBar(parent, name)
+{
+ QToolTip::add(this, i18n("<img src=\"available_color\">&nbsp; - Available Chunks<br><img src=\"unavailable_color\">&nbsp; - Unavailable Chunks<br><img src=\"excluded_color\">&nbsp; - Excluded Chunks"));
+}
+
+
+AvailabilityChunkBar::~AvailabilityChunkBar()
+{
+}
+
+
+const bt::BitSet & AvailabilityChunkBar::getBitSet() const
+{
+ if (curr_tc)
+ return curr_tc->availableChunksBitSet();
+ else
+ return bt::BitSet::null;
+}
+}
+
+#include "availabilitychunkbar.moc"
diff --git a/plugins/infowidget/availabilitychunkbar.h b/plugins/infowidget/availabilitychunkbar.h
new file mode 100644
index 0000000..a5ae625
--- /dev/null
+++ b/plugins/infowidget/availabilitychunkbar.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef AVAILABILITYCHUNKBAR_H
+#define AVAILABILITYCHUNKBAR_H
+
+#include "chunkbar.h"
+
+namespace kt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class AvailabilityChunkBar : public ChunkBar
+ {
+ Q_OBJECT
+ public:
+ AvailabilityChunkBar(QWidget* parent, const char* name);
+ virtual ~AvailabilityChunkBar();
+
+ virtual const bt::BitSet & getBitSet() const;
+
+ };
+}
+
+#endif
diff --git a/plugins/infowidget/chunkbar.cpp b/plugins/infowidget/chunkbar.cpp
new file mode 100644
index 0000000..a30571a
--- /dev/null
+++ b/plugins/infowidget/chunkbar.cpp
@@ -0,0 +1,312 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Vincent Wagelaar <vincent@ricardis.tudelft.nl> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qpainter.h>
+#include <qpen.h>
+#include <qbrush.h>
+#include <qvaluelist.h>
+#include <qpixmap.h>
+#include <math.h>
+#include <qtooltip.h>
+#include <klocale.h>
+#include <qmime.h>
+#include <qimage.h>
+#include <util/log.h>
+#include <interfaces/torrentinterface.h>
+#include <util/bitset.h>
+#include <torrent/globals.h>
+#include "chunkbar.h"
+
+using namespace bt;
+using namespace kt;
+
+namespace kt
+{
+
+ struct Range
+ {
+ int first,last;
+ int fac;
+ };
+
+
+ static void FillAndFrameBlack(QImage* image, uint color, int size)
+ {
+ image->fill(color);
+ for (int i = 0; i < size; i++)
+ {
+ image->setPixel(0, i, 0);
+ image->setPixel(size - 1, i, 0);
+ image->setPixel(i, 0, 0);
+ image->setPixel(i, size - 1, 0);
+ }
+ }
+
+
+
+ static void InitializeToolTipImages(ChunkBar* bar)
+ {
+ static bool images_initialized = false;
+ if (images_initialized)
+ return;
+ images_initialized = true;
+
+ QMimeSourceFactory* factory = QMimeSourceFactory::defaultFactory();
+
+ QImage excluded(16, 16, 32);
+ FillAndFrameBlack(&excluded, bar->colorGroup().color(QColorGroup::Mid).pixel(), 16);
+ factory->setImage("excluded_color", excluded);
+
+ QImage available(16, 16, 32);
+ FillAndFrameBlack(&available, bar->colorGroup().highlight().pixel(), 16);
+ factory->setImage("available_color", available);
+
+ QImage unavailable(16, 16, 32);
+ FillAndFrameBlack(&unavailable, bar->colorGroup().base().pixel(), 16);
+ factory->setImage("unavailable_color", unavailable);
+ }
+
+ ChunkBar::ChunkBar(QWidget *parent, const char *name)
+ : QFrame(parent, name),curr_tc(0)
+ {
+ setFrameShape(StyledPanel);
+ setFrameShadow(Sunken);
+ setLineWidth(3);
+ setMidLineWidth(3);
+
+ show_excluded = false;
+
+ InitializeToolTipImages(this);
+
+ QToolTip::add(this, i18n("<img src=\"available_color\">&nbsp; - Downloaded Chunks<br><img src=\"unavailable_color\">&nbsp; - Chunks to Download<br><img src=\"excluded_color\">&nbsp; - Excluded Chunks"));
+
+ }
+
+
+ ChunkBar::~ChunkBar()
+ {}
+
+ void ChunkBar::updateBar()
+ {
+ const BitSet & bs = getBitSet();
+ QSize s = contentsRect().size();
+ bool changed = !(curr == bs);
+ if (show_excluded && curr_tc)
+ {
+ BitSet ebs = curr_tc->excludedChunksBitSet();
+ ebs.orBitSet(curr_tc->onlySeedChunksBitSet()),
+ changed = changed || !(curr_ebs == ebs);
+ curr_ebs = ebs;
+ }
+
+ if (changed || pixmap.isNull() || pixmap.width() != s.width())
+ {
+ // PROFILE("ChunkBar::updateBar");
+ // Out() << "Pixmap : " << s.width() << " " << s.height() << endl;
+ pixmap.resize(s);
+ pixmap.fill(colorGroup().color(QColorGroup::Base));
+ QPainter painter(&pixmap);
+ drawBarContents(&painter);
+ update();
+ }
+ }
+
+ void ChunkBar::drawContents(QPainter *p)
+ {
+ // first draw background
+ if (isEnabled())
+ p->setBrush(colorGroup().base());
+ else
+ p->setBrush(colorGroup().background());
+
+ p->setPen(Qt::NoPen);
+ p->drawRect(contentsRect());
+ if (isEnabled())
+ p->drawPixmap(contentsRect(),pixmap);
+ }
+
+ void ChunkBar::setTC(kt::TorrentInterface* tc)
+ {
+ curr_tc = tc;
+ QSize s = contentsRect().size();
+ //Out() << "Pixmap : " << s.width() << " " << s.height() << endl;
+ pixmap.resize(s);
+ pixmap.fill(colorGroup().color(QColorGroup::Base));
+ QPainter painter(&pixmap);
+ drawBarContents(&painter);
+ update();
+ }
+
+ void ChunkBar::drawBarContents(QPainter *p)
+ {
+ p->saveWorldMatrix();
+ if (curr_tc)
+ {
+ const TorrentStats & s = curr_tc->getStats();
+ Uint32 w = contentsRect().width();
+ const BitSet & bs = getBitSet();
+ curr = bs;
+ if (bs.allOn())
+ drawAllOn(p,colorGroup().highlight());
+ else if (s.total_chunks > w)
+ drawMoreChunksThenPixels(p,bs,colorGroup().highlight());
+ else
+ drawEqual(p,bs,colorGroup().highlight());
+
+ if (show_excluded && s.num_chunks_excluded > 0)
+ {
+ QColor c = colorGroup().color(QColorGroup::Mid);
+ if (curr_ebs.allOn())
+ drawAllOn(p,c);
+ else if (s.total_chunks > w)
+ drawMoreChunksThenPixels(p,curr_ebs,c);
+ else
+ drawEqual(p,curr_ebs,c);
+ }
+ }
+ p->restoreWorldMatrix();
+ }
+
+ void ChunkBar::drawEqual(QPainter *p,const BitSet & bs,const QColor & color)
+ {
+ //p->setPen(QPen(colorGroup().highlight(),1,Qt::SolidLine));
+ QColor c = color;
+
+ Uint32 w = contentsRect().width();
+ double scale = 1.0;
+ Uint32 total_chunks = curr_tc->getStats().total_chunks;
+ if (curr_tc->getStats().total_chunks != w)
+ scale = (double)w / total_chunks;
+
+ p->setPen(QPen(c,1,Qt::SolidLine));
+ p->setBrush(c);
+
+ QValueList<Range> rs;
+
+ for (Uint32 i = 0;i < bs.getNumBits();i++)
+ {
+ if (!bs.get(i))
+ continue;
+
+ if (rs.empty())
+ {
+ Range r = {i,i,0};
+ rs.append(r);
+ }
+ else
+ {
+ Range & l = rs.last();
+ if (l.last == int(i - 1))
+ {
+ l.last = i;
+ }
+ else
+ {
+ Range r = {i,i,0};
+ rs.append(r);
+ }
+ }
+ }
+
+ QRect r = contentsRect();
+
+ for (QValueList<Range>::iterator i = rs.begin();i != rs.end();++i)
+ {
+ Range & ra = *i;
+ int rw = ra.last - ra.first + 1;
+ p->drawRect((int)(scale * ra.first),0,(int)(rw * scale),r.height());
+ }
+ }
+
+ void ChunkBar::drawMoreChunksThenPixels(QPainter *p,const BitSet & bs,const QColor & color)
+ {
+ Uint32 w = contentsRect().width();
+ double chunks_per_pixel = (double)bs.getNumBits() / w;
+ QValueList<Range> rs;
+
+ for (Uint32 i = 0;i < w;i++)
+ {
+ Uint32 num_dl = 0;
+ Uint32 jStart = (Uint32) (i*chunks_per_pixel);
+ Uint32 jEnd = (Uint32) ((i+1)*chunks_per_pixel+0.5);
+ for (Uint32 j = jStart;j < jEnd;j++)
+ if (bs.get(j))
+ num_dl++;
+
+ if (num_dl == 0)
+ continue;
+
+ int fac = int(100*((double)num_dl / (jEnd - jStart)) + 0.5);
+ if (rs.empty())
+ {
+ Range r = {i,i,fac};
+ rs.append(r);
+ }
+ else
+ {
+ Range & l = rs.last();
+ if (l.last == int(i - 1) && l.fac == fac)
+ {
+ l.last = i;
+ }
+ else
+ {
+ Range r = {i,i,fac};
+ rs.append(r);
+ }
+ }
+ }
+
+ QRect r = contentsRect();
+
+ for (QValueList<Range>::iterator i = rs.begin();i != rs.end();++i)
+ {
+ Range & ra = *i;
+ int rw = ra.last - ra.first + 1;
+ int fac = ra.fac;
+ QColor c = color;
+ if (fac < 100)
+ {
+ // do some rounding off
+ if (fac <= 25)
+ fac = 25;
+ else if (fac <= 50)
+ fac = 45;
+ else
+ fac = 65;
+ c = color.light(200-fac);
+ }
+ p->setPen(QPen(c,1,Qt::SolidLine));
+ p->setBrush(c);
+ p->drawRect(ra.first,0,rw,r.height());
+ }
+
+ }
+
+ void ChunkBar::drawAllOn(QPainter *p,const QColor & color)
+ {
+ p->setPen(QPen(color,1,Qt::SolidLine));
+ p->setBrush(color);
+ QSize s = contentsRect().size();
+ p->drawRect(0,0,s.width(),s.height());
+ }
+}
+
+#include "chunkbar.moc"
diff --git a/plugins/infowidget/chunkbar.h b/plugins/infowidget/chunkbar.h
new file mode 100644
index 0000000..6cf0d6a
--- /dev/null
+++ b/plugins/infowidget/chunkbar.h
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Vincent Wagelaar <vincent@ricardis.tudelft.nl> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef CHUNKBAR_H
+#define CHUNKBAR_H
+
+#include <qlabel.h>
+#include <util/bitset.h>
+#include <qpixmap.h>
+
+class QPainter;
+
+namespace kt
+{
+ class TorrentInterface;
+}
+
+namespace bt
+{
+ class BitSet;
+}
+
+namespace kt
+{
+
+ /**
+ * @author Joris Guisson, Vincent Wagelaar
+ *
+ * Bar which displays BitSets, subclasses need to fill the BitSet.
+ * BitSets can represent which chunks are downloaded, which chunks are available
+ * and which chunks are excluded.
+ */
+ class ChunkBar : public QFrame
+ {
+ Q_OBJECT
+ public:
+ ChunkBar(QWidget *parent = 0, const char *name = 0);
+ virtual ~ChunkBar();
+
+ void setTC(kt::TorrentInterface* tc);
+
+ virtual const bt::BitSet & getBitSet() const = 0;
+ virtual void drawContents(QPainter *p);
+ virtual void updateBar();
+
+ private:
+ void drawEqual(QPainter *p,const bt::BitSet & bs,const QColor & color);
+ void drawMoreChunksThenPixels(QPainter *p,const bt::BitSet & bs,const QColor & color);
+ void drawAllOn(QPainter *p,const QColor & color);
+ void drawBarContents(QPainter *p);
+
+ protected:
+ kt::TorrentInterface* curr_tc;
+ bool show_excluded;
+ bt::BitSet curr,curr_ebs;
+ QPixmap pixmap;
+ };
+}
+
+#endif
diff --git a/plugins/infowidget/chunkdownloadview.cpp b/plugins/infowidget/chunkdownloadview.cpp
new file mode 100644
index 0000000..276a311
--- /dev/null
+++ b/plugins/infowidget/chunkdownloadview.cpp
@@ -0,0 +1,166 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <qlabel.h>
+#include <interfaces/chunkdownloadinterface.h>
+#include <interfaces/functions.h>
+#include <interfaces/torrentinterface.h>
+#include "chunkdownloadview.h"
+
+
+using namespace bt;
+using namespace kt;
+
+namespace kt
+{
+
+ ChunkDownloadViewItem::ChunkDownloadViewItem(KListView* cdv,kt::ChunkDownloadInterface* cd)
+ : KListViewItem(cdv),cd(cd)
+ {
+ update();
+ }
+
+ void ChunkDownloadViewItem::update()
+ {
+ ChunkDownloadInterface::Stats s;
+ cd->getStats(s);
+
+ setText(0,QString::number(s.chunk_index));
+ setText(1,QString("%1 / %2").arg(s.pieces_downloaded).arg(s.total_pieces));
+ setText(2,s.current_peer_id);
+ setText(3,KBytesPerSecToString(s.download_speed / 1024.0));
+ setText(4,QString::number(s.num_downloaders));
+ }
+
+ int ChunkDownloadViewItem::compare(QListViewItem * i,int col,bool) const
+ {
+ ChunkDownloadViewItem* it = (ChunkDownloadViewItem*)i;
+ kt::ChunkDownloadInterface* ocd = it->cd;
+ ChunkDownloadInterface::Stats s;
+ cd->getStats(s);
+ ChunkDownloadInterface::Stats os;
+ ocd->getStats(os);
+ switch (col)
+ {
+ case 0: return CompareVal(s.chunk_index,os.chunk_index);
+ case 1: return CompareVal(s.pieces_downloaded,os.pieces_downloaded);
+ case 2: return QString::compare(s.current_peer_id,os.current_peer_id);
+ case 3: return CompareVal(s.download_speed,os.download_speed);
+ case 4: return CompareVal(s.num_downloaders,os.num_downloaders);
+ }
+ return 0;
+ }
+
+
+ ChunkDownloadView::ChunkDownloadView(QWidget *parent, const char *name)
+ : ChunkDownloadViewBase(parent, name)
+ {
+ m_list_view->setShowSortIndicator(true);
+ m_list_view->setAllColumnsShowFocus(true);
+
+ m_list_view->setColumnAlignment(0,Qt::AlignLeft);
+ m_list_view->setColumnAlignment(1,Qt::AlignCenter);
+ m_list_view->setColumnAlignment(3,Qt::AlignRight);
+ m_list_view->setColumnAlignment(4,Qt::AlignRight);
+ curr_tc = 0;
+ }
+
+
+ ChunkDownloadView::~ChunkDownloadView()
+ {}
+
+
+ void ChunkDownloadView::addDownload(kt::ChunkDownloadInterface* cd)
+ {
+ ChunkDownloadViewItem* it = new ChunkDownloadViewItem(m_list_view,cd);
+ items.insert(cd,it);
+ }
+
+ void ChunkDownloadView::removeDownload(kt::ChunkDownloadInterface* cd)
+ {
+ if (!items.contains(cd))
+ return;
+
+ ChunkDownloadViewItem* it = items[cd];
+ delete it;
+ items.remove(cd);
+ }
+
+ void ChunkDownloadView::removeAll()
+ {
+ m_list_view->clear();
+ items.clear();
+ }
+
+ void ChunkDownloadView::changeTC(kt::TorrentInterface* tc)
+ {
+ curr_tc = tc;
+ setEnabled(curr_tc != 0);
+ update();
+ }
+
+ void ChunkDownloadView::update()
+ {
+ if (!curr_tc)
+ return;
+
+ QMap<ChunkDownloadInterface*,ChunkDownloadViewItem*>::iterator i = items.begin();
+ while (i != items.end())
+ {
+ ChunkDownloadViewItem* it = i.data();
+ it->update();
+ i++;
+ }
+ m_list_view->sort();
+
+ const TorrentStats & s = curr_tc->getStats();
+ m_chunks_downloading->setText(QString::number(s.num_chunks_downloading));
+ m_chunks_downloaded->setText(QString::number(s.num_chunks_downloaded));
+ m_total_chunks->setText(QString::number(s.total_chunks));
+ m_excluded_chunks->setText(QString::number(s.num_chunks_excluded));
+ m_chunks_left->setText(QString::number(s.num_chunks_left));
+
+ if( s.chunk_size / 1024 < 1024 )
+ m_size_chunks->setText(QString::number(s.chunk_size / 1024) + "." + QString::number((s.chunk_size % 1024) / 100) + " KB");
+ else
+ m_size_chunks->setText(QString::number(s.chunk_size / 1024 / 1024) + "." + QString::number(((s.chunk_size / 1024) % 1024) / 100) + " MB");
+ }
+
+ void ChunkDownloadView::saveLayout(KConfig* cfg,const QString & group_name)
+ {
+ m_list_view->saveLayout(cfg,group_name);
+ }
+
+ void ChunkDownloadView::restoreLayout(KConfig* cfg,const QString & group_name)
+ {
+ m_list_view->restoreLayout(cfg,group_name);
+ }
+
+ void ChunkDownloadView::clear()
+ {
+ m_chunks_downloading->clear();
+ m_chunks_downloaded->clear();
+ m_total_chunks->clear();
+ m_excluded_chunks->clear();
+ m_size_chunks->clear();
+ }
+}
+
+#include "chunkdownloadview.moc"
diff --git a/plugins/infowidget/chunkdownloadview.h b/plugins/infowidget/chunkdownloadview.h
new file mode 100644
index 0000000..a00a746
--- /dev/null
+++ b/plugins/infowidget/chunkdownloadview.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTCHUNKDOWNLOADVIEW_H
+#define BTCHUNKDOWNLOADVIEW_H
+
+#include <klistview.h>
+#include <qmap.h>
+#include "chunkdownloadviewbase.h"
+
+namespace kt
+{
+ class ChunkDownloadInterface;
+ class ChunkDownloadView;
+
+ class ChunkDownloadViewItem : public KListViewItem
+ {
+ kt::ChunkDownloadInterface* cd;
+ public:
+ ChunkDownloadViewItem(KListView* cdv,kt::ChunkDownloadInterface* cd);
+
+ void update();
+ int compare(QListViewItem * i,int col,bool) const;
+ };
+
+
+ /**
+ @author Joris Guisson
+ */
+ class ChunkDownloadView : public ChunkDownloadViewBase
+ {
+ Q_OBJECT
+
+ QMap<kt::ChunkDownloadInterface*,ChunkDownloadViewItem*> items;
+ kt::TorrentInterface* curr_tc;
+ public:
+ ChunkDownloadView(QWidget *parent = 0, const char *name = 0);
+ virtual ~ChunkDownloadView();
+
+ void saveLayout(KConfig* cfg,const QString & group_name);
+ void restoreLayout(KConfig* cfg,const QString & group_name);
+ void clear();
+ void update();
+ void changeTC(kt::TorrentInterface* tc);
+
+ public slots:
+ void addDownload(kt::ChunkDownloadInterface* cd);
+ void removeDownload(kt::ChunkDownloadInterface* cd);
+ void removeAll();
+ };
+}
+
+
+#endif
diff --git a/plugins/infowidget/chunkdownloadviewbase.ui b/plugins/infowidget/chunkdownloadviewbase.ui
new file mode 100644
index 0000000..24a6e18
--- /dev/null
+++ b/plugins/infowidget/chunkdownloadviewbase.ui
@@ -0,0 +1,345 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ChunkDownloadViewBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ChunkDownloadViewBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>830</width>
+ <height>534</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Chunks</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Total:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_total_chunks</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Currently downloading:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_chunks_downloading</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Downloaded:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_chunks_downloaded</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout38</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_4</cstring>
+ </property>
+ <property name="text">
+ <string>Excluded:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_excluded_chunks</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout38_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Left:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_chunks_left</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout24</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_8</cstring>
+ </property>
+ <property name="text">
+ <string>Size:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_size_chunks</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Chunk</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Progress</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Peer</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Down Speed</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Assigned Peers</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_list_view</cstring>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/infowidget/downloadedchunkbar.cpp b/plugins/infowidget/downloadedchunkbar.cpp
new file mode 100644
index 0000000..ab87ff7
--- /dev/null
+++ b/plugins/infowidget/downloadedchunkbar.cpp
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/bitset.h>
+#include <interfaces/torrentinterface.h>
+#include "downloadedchunkbar.h"
+
+namespace kt
+{
+
+ DownloadedChunkBar::DownloadedChunkBar(QWidget* parent, const char* name)
+ : ChunkBar(parent,name)
+ {
+ show_excluded = true;
+ }
+
+
+ DownloadedChunkBar::~DownloadedChunkBar()
+ {}
+
+
+ const bt::BitSet & DownloadedChunkBar::getBitSet() const
+ {
+ if (curr_tc)
+ return curr_tc->downloadedChunksBitSet();
+ else
+ return bt::BitSet::null;
+ }
+}
+
+#include "downloadedchunkbar.moc"
diff --git a/plugins/infowidget/downloadedchunkbar.h b/plugins/infowidget/downloadedchunkbar.h
new file mode 100644
index 0000000..996634f
--- /dev/null
+++ b/plugins/infowidget/downloadedchunkbar.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef DOWNLOADEDCHUNKBAR_H
+#define DOWNLOADEDCHUNKBAR_H
+
+#include "chunkbar.h"
+
+namespace kt
+{
+ /**
+ @author Joris Guisson
+ */
+ class DownloadedChunkBar : public ChunkBar
+ {
+ Q_OBJECT
+ public:
+ DownloadedChunkBar(QWidget* parent, const char* name);
+ virtual ~DownloadedChunkBar();
+
+ virtual const bt::BitSet & getBitSet() const;
+
+ };
+}
+
+#endif
diff --git a/plugins/infowidget/fileview.cpp b/plugins/infowidget/fileview.cpp
new file mode 100644
index 0000000..7fb56a6
--- /dev/null
+++ b/plugins/infowidget/fileview.cpp
@@ -0,0 +1,447 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kglobal.h>
+#include <kpopupmenu.h>
+#include <krun.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <util/bitset.h>
+#include <util/functions.h>
+#include <interfaces/functions.h>
+#include <interfaces/torrentinterface.h>
+#include <interfaces/torrentfileinterface.h>
+#include <qfileinfo.h>
+#include "functions.h"
+#include "iwfiletreeitem.h"
+#include "iwfiletreediritem.h"
+#include "fileview.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ FileView::FileView(QWidget *parent, const char *name)
+ : KListView(parent, name),curr_tc(0),multi_root(0),pending_fill(0),next_fill_item(0)
+ {
+ setFrameShape(QFrame::NoFrame);
+ addColumn( i18n( "File" ) );
+ addColumn( i18n( "Size" ) );
+ addColumn( i18n( "Download" ) );
+ addColumn( i18n( "Preview" ) );
+ addColumn( i18n( "% Complete" ) );
+ setShowSortIndicator(true);
+
+ context_menu = new KPopupMenu(this);
+ preview_id = context_menu->insertItem(SmallIcon("fileopen"),i18n("Open"));
+ context_menu->insertSeparator();
+ first_id = context_menu->insertItem(i18n("Download First"));
+ normal_id = context_menu->insertItem(i18n("Download Normally"));
+ last_id = context_menu->insertItem(i18n("Download Last"));
+ context_menu->insertSeparator();
+ dnd_keep_id = context_menu->insertItem(i18n("Do Not Download"));
+ dnd_throw_away_id = context_menu->insertItem(i18n("Delete File(s)"));
+
+
+ context_menu->setItemEnabled(preview_id, false);
+ context_menu->setItemEnabled(first_id, false);
+ context_menu->setItemEnabled(normal_id, false);
+ context_menu->setItemEnabled(last_id, false);
+ context_menu->setItemEnabled(dnd_keep_id, false);
+ context_menu->setItemEnabled(dnd_throw_away_id, false);
+
+ connect(this,SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint& )),
+ this,SLOT(showContextMenu(KListView*, QListViewItem*, const QPoint& )));
+ connect(context_menu, SIGNAL ( activated ( int ) ), this, SLOT ( contextItem ( int ) ) );
+ connect(this,SIGNAL(doubleClicked( QListViewItem*, const QPoint&, int )),
+ this,SLOT(onDoubleClicked(QListViewItem*, const QPoint&, int)));
+
+ connect(&fill_timer, SIGNAL(timeout()), this, SLOT( fillTreePartial() ) );
+
+ setEnabled(false);
+
+ setSelectionMode(QListView::Extended);
+ }
+
+
+ FileView::~FileView()
+ {}
+
+#define ITEMS_PER_TICK 100
+
+ void FileView::fillTreePartial()
+ {
+ int cnt = 0;
+ while (next_fill_item < curr_tc->getNumFiles() && cnt < ITEMS_PER_TICK)
+ {
+ TorrentFileInterface & file = curr_tc->getTorrentFile(next_fill_item);
+ multi_root->insert(file.getPath(),file);
+ cnt++;
+ next_fill_item++;
+ }
+
+ if (next_fill_item >= curr_tc->getNumFiles())
+ {
+ multi_root->setOpen(true);
+ setRootIsDecorated(true);
+ setEnabled(true);
+ multi_root->updatePriorityInformation(curr_tc);
+ multi_root->updatePercentageInformation();
+ multi_root->updatePreviewInformation(curr_tc);
+ fill_timer.stop();
+ connect(curr_tc,SIGNAL(missingFilesMarkedDND( kt::TorrentInterface* )),
+ this,SLOT(refreshFileTree( kt::TorrentInterface* )));
+ }
+ else
+ fill_timer.start(0,true);
+ }
+
+ void FileView::fillFileTree()
+ {
+ multi_root = 0;
+ clear();
+
+ if (!curr_tc)
+ return;
+
+ if (curr_tc->getStats().multi_file_torrent)
+ {
+ setEnabled(false);
+ multi_root = new IWFileTreeDirItem(this,curr_tc->getStats().torrent_name);
+ next_fill_item = 0;
+ fillTreePartial();
+ }
+ else
+ {
+ const TorrentStats & s = curr_tc->getStats();
+ this->setRootIsDecorated(false);
+ KListViewItem* item = new KListViewItem(
+ this,
+ s.torrent_name,
+ BytesToString(s.total_bytes));
+
+ item->setPixmap(0,KMimeType::findByPath(s.torrent_name)->pixmap(KIcon::Small));
+ setEnabled(true);
+ connect(curr_tc,SIGNAL(missingFilesMarkedDND( kt::TorrentInterface* )),
+ this,SLOT(refreshFileTree( kt::TorrentInterface* )));
+ }
+ }
+
+ void FileView::changeTC(kt::TorrentInterface* tc)
+ {
+ if (tc == curr_tc)
+ return;
+
+ curr_tc = tc;
+ pending_fill = true;
+ fill_timer.stop();
+ fillFileTree();
+ }
+
+ void FileView::update()
+ {
+ if (!curr_tc)
+ return;
+
+ if (isVisible() && !pending_fill)
+ {
+ readyPreview();
+ readyPercentage();
+ }
+ }
+
+ void FileView::readyPercentage()
+ {
+ if (curr_tc && !curr_tc->getStats().multi_file_torrent)
+ {
+ QListViewItemIterator it(this);
+ if (!it.current())
+ return;
+
+ const BitSet & bs = curr_tc->downloadedChunksBitSet();
+ Uint32 total = bs.getNumBits();
+ Uint32 on = bs.numOnBits();
+ double percent = 100.0 * ((double)on/(double)total);
+ if (percent < 0.0)
+ percent = 0.0;
+ else if (percent > 100.0)
+ percent = 100.0;
+ KLocale* loc = KGlobal::locale();
+ it.current()->setText(4,i18n("%1 %").arg(loc->formatNumber(percent,2)));
+ }
+ }
+
+ void FileView::readyPreview()
+ {
+ if (curr_tc && !curr_tc->getStats().multi_file_torrent)
+ {
+ QListViewItemIterator it(this);
+ if (!it.current())
+ return;
+
+ if (IsMultimediaFile(curr_tc->getStats().output_path))
+ {
+ if ( curr_tc->readyForPreview() )
+ it.current()->setText(3, i18n("Available"));
+ else
+ it.current()->setText(3, i18n("Pending"));
+ }
+ else
+ it.current()->setText(3, i18n("No"));
+
+ }
+ }
+
+ void FileView::showContextMenu(KListView* ,QListViewItem*,const QPoint & p)
+ {
+ const TorrentStats & s = curr_tc->getStats();
+ // don't show a menu if item is 0 or if it is a directory
+
+
+
+ QPtrList<QListViewItem> sel = selectedItems();
+ switch(sel.count())
+ {
+ case 0:
+ return;
+ break;
+ case 1:
+ break;
+ default:
+ context_menu->setItemEnabled(first_id, true);
+ context_menu->setItemEnabled(normal_id, true);
+ context_menu->setItemEnabled(last_id, true);
+ context_menu->setItemEnabled(preview_id, false);
+ context_menu->setItemEnabled(dnd_keep_id,true);
+ context_menu->setItemEnabled(dnd_throw_away_id,true);
+ context_menu->popup(p);
+ return;
+ break;
+ }
+ QListViewItem* item = sel.getFirst();
+
+ context_menu->setItemEnabled(first_id, false);
+ context_menu->setItemEnabled(normal_id, false);
+ context_menu->setItemEnabled(last_id, false);
+ if (s.multi_file_torrent && item->childCount() == 0)
+ {
+ kt::TorrentFileInterface & file = ((FileTreeItem*)item)->getTorrentFile();
+ if (!file.isNull())
+ {
+ context_menu->setItemEnabled(preview_id, true);
+ this->preview_path = "cache" + bt::DirSeparator() + file.getPath();
+
+ switch(file.getPriority())
+ {
+ case FIRST_PRIORITY:
+ context_menu->setItemEnabled(normal_id, true);
+ context_menu->setItemEnabled(last_id, true);
+ context_menu->setItemEnabled(dnd_keep_id,true);
+ context_menu->setItemEnabled(dnd_keep_id,true);
+ context_menu->setItemEnabled(dnd_throw_away_id,true);
+ break;
+ case LAST_PRIORITY:
+ context_menu->setItemEnabled(first_id, true);
+ context_menu->setItemEnabled(normal_id, true);
+ context_menu->setItemEnabled(dnd_keep_id,true);
+ context_menu->setItemEnabled(dnd_keep_id,true);
+ context_menu->setItemEnabled(dnd_throw_away_id,true);
+ break;
+ case EXCLUDED:
+ context_menu->setItemEnabled(first_id,true);
+ context_menu->setItemEnabled(normal_id,true);
+ context_menu->setItemEnabled(last_id, true);
+ context_menu->setItemEnabled(dnd_keep_id,true);
+ context_menu->setItemEnabled(dnd_throw_away_id,false);
+ break;
+ case ONLY_SEED_PRIORITY:
+ context_menu->setItemEnabled(first_id,true);
+ context_menu->setItemEnabled(normal_id,true);
+ context_menu->setItemEnabled(last_id, true);
+ context_menu->setItemEnabled(dnd_keep_id,false);
+ context_menu->setItemEnabled(dnd_throw_away_id,true);
+ break;
+ case PREVIEW_PRIORITY:
+ default:
+ context_menu->setItemEnabled(first_id, true);
+ context_menu->setItemEnabled(normal_id,false);
+ context_menu->setItemEnabled(last_id, true);
+ context_menu->setItemEnabled(dnd_keep_id,true);
+ context_menu->setItemEnabled(dnd_throw_away_id,true);
+ break;
+ }
+ }
+ else
+ {
+ context_menu->setItemEnabled(preview_id, false);
+ }
+ }
+ else
+ {
+ bool val = item->childCount() != 0;
+ context_menu->setItemEnabled(first_id, val);
+ context_menu->setItemEnabled(normal_id, val);
+ context_menu->setItemEnabled(last_id, val);
+ context_menu->setItemEnabled(dnd_keep_id,val);
+ context_menu->setItemEnabled(dnd_throw_away_id,val);
+
+ context_menu->setItemEnabled(preview_id, true);
+ if (s.multi_file_torrent)
+ {
+ FileTreeDirItem* dir = ((FileTreeDirItem*)item);
+ preview_path = "cache" + dir->getPath();
+ }
+ else
+ {
+ preview_path = "cache";
+ }
+ }
+
+ context_menu->popup(p);
+ }
+
+ void FileView::contextItem(int id)
+ {
+ QPtrList<QListViewItem> sel = selectedItems();
+
+ Priority newpriority = NORMAL_PRIORITY;
+ if(id == this->preview_id)
+ {
+ new KRun(KURL::fromPathOrURL(this->curr_tc->getTorDir()+preview_path), 0, true, true);
+ return;
+ }
+ else if (id == dnd_throw_away_id)
+ {
+ Uint32 n = sel.count();
+ if (n == 1) // single item can be a directory
+ {
+ // the number of the beast > 1
+ n = (*sel.begin())->childCount() == 0 ? 1 : 666;
+ }
+
+ QString msg = i18n(
+ "You will lose all data in this file, are you sure you want to do this ?",
+ "You will lose all data in these files, are you sure you want to do this ?",n);
+
+ if (KMessageBox::warningYesNo(0,msg) == KMessageBox::No)
+ return;
+ newpriority = EXCLUDED;
+ }
+ else if(id == this->first_id)
+ {
+ newpriority = FIRST_PRIORITY;
+ }
+ else if(id == this->last_id)
+ {
+ newpriority = LAST_PRIORITY;
+ }
+ else if(id == this->normal_id)
+ {
+ newpriority = NORMAL_PRIORITY;
+ }
+ else if (id == dnd_keep_id)
+ {
+ newpriority = ONLY_SEED_PRIORITY;
+ }
+
+
+
+ QPtrList<QListViewItem>::Iterator i = sel.begin();
+ while(i != sel.end())
+ {
+ QListViewItem* item = *i;
+ changePriority(item, newpriority);
+ multi_root->updatePriorityInformation(curr_tc);
+ i++;
+ }
+ }
+
+ void FileView::changePriority(QListViewItem* item, Priority newpriority)
+ {
+ if(item->childCount() == 0)
+ {
+ FileTreeItem* fti = (FileTreeItem*)item;
+ if (newpriority == EXCLUDED)
+ {
+ fti->setChecked(false,false);
+ }
+ else if (newpriority == ONLY_SEED_PRIORITY)
+ {
+ fti->setChecked(false,true);
+ }
+ else
+ {
+ if (!fti->isOn())
+ fti->setChecked(true,true);
+ fti->getTorrentFile().setPriority(newpriority);
+ }
+ return;
+ }
+ QListViewItem* myChild = item->firstChild();
+ while( myChild )
+ {
+ changePriority(myChild, newpriority);
+ myChild = myChild->nextSibling();
+ }
+ }
+
+ void FileView::refreshFileTree(kt::TorrentInterface* tc)
+ {
+ if (!tc || curr_tc != tc)
+ return;
+
+ if (multi_root)
+ multi_root->updateDNDInformation();
+ }
+
+ void FileView::onDoubleClicked(QListViewItem* item,const QPoint & ,int )
+ {
+ if (!curr_tc)
+ return;
+
+ const TorrentStats & s = curr_tc->getStats();
+
+ if (s.multi_file_torrent)
+ {
+ if (item->childCount() == 0)
+ {
+ // file
+ FileTreeItem* file = (FileTreeItem*)item;
+ QString path = "cache" + bt::DirSeparator() + file->getTorrentFile().getPath();
+ new KRun(KURL::fromPathOrURL(curr_tc->getTorDir() + path), 0, true, true);
+ }
+ else
+ {
+ // directory
+ FileTreeDirItem* dir = ((FileTreeDirItem*)item);
+ new KRun(KURL::fromPathOrURL(curr_tc->getTorDir() + "cache" + dir->getPath()), 0, true, true);
+ }
+ }
+ else
+ {
+ QFileInfo fi(curr_tc->getTorDir()+"cache");
+ new KRun(KURL::fromPathOrURL(fi.readLink()), 0, true, true);
+ }
+ }
+}
+
+#include "fileview.moc"
diff --git a/plugins/infowidget/fileview.h b/plugins/infowidget/fileview.h
new file mode 100644
index 0000000..6a827dc
--- /dev/null
+++ b/plugins/infowidget/fileview.h
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTFILEVIEW_H
+#define KTFILEVIEW_H
+
+#include <klistview.h>
+#include <util/constants.h>
+#include <qtimer.h>
+
+namespace kt
+{
+ class TorrentInterface;
+ class IWFileTreeDirItem;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class FileView : public KListView
+ {
+ Q_OBJECT
+ public:
+ FileView(QWidget *parent = 0, const char *name = 0);
+ virtual ~FileView();
+
+ void update();
+ void changeTC(kt::TorrentInterface* tc);
+ private slots:
+ void contextItem(int id);
+ void showContextMenu(KListView* ,QListViewItem* item,const QPoint & p);
+ void refreshFileTree(kt::TorrentInterface* tc);
+ void onDoubleClicked(QListViewItem* item,const QPoint & ,int );
+ void fillTreePartial();
+
+ private:
+ void fillFileTree();
+ void readyPreview();
+ void readyPercentage();
+ void changePriority(QListViewItem* item, bt::Priority newpriority);
+
+ private:
+ kt::TorrentInterface* curr_tc;
+ IWFileTreeDirItem* multi_root;
+ bool pending_fill;
+ KPopupMenu* context_menu;
+ QString preview_path;
+ QTimer fill_timer;
+ int preview_id;
+ int first_id;
+ int normal_id;
+ int last_id;
+ int dnd_keep_id;
+ int dnd_throw_away_id;
+
+ int next_fill_item;
+ };
+
+}
+
+#endif
diff --git a/plugins/infowidget/flagdb.cpp b/plugins/infowidget/flagdb.cpp
new file mode 100644
index 0000000..140a3b7
--- /dev/null
+++ b/plugins/infowidget/flagdb.cpp
@@ -0,0 +1,119 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Modestas Vainius <modestas@vainius.eu> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <qfile.h>
+#include <qimage.h>
+#include <kstandarddirs.h>
+#include "flagdb.h"
+
+kt::FlagDBSource::FlagDBSource(const char* type, const QString& pathPattern)
+ : type(type), pathPattern(pathPattern)
+{
+}
+
+kt::FlagDBSource::FlagDBSource(const QString& pathPattern)
+ : type(NULL), pathPattern(pathPattern)
+{
+}
+
+kt::FlagDBSource::FlagDBSource()
+ : type(NULL), pathPattern()
+{
+}
+
+QString kt::FlagDBSource::FlagDBSource::getPath(const QString& country) const
+{
+ if (type) {
+ return locate(type, pathPattern.arg(country));
+ } else {
+ return pathPattern.arg(country);
+ }
+}
+
+const QPixmap& kt::FlagDB::nullPixmap = QPixmap();
+
+kt::FlagDB::FlagDB(int preferredWidth, int preferredHeight)
+ : preferredWidth(preferredWidth),
+ preferredHeight(preferredHeight),
+ sources(),
+ db()
+{
+}
+
+
+kt::FlagDB::FlagDB(const FlagDB& other)
+ : preferredWidth(other.preferredWidth),
+ preferredHeight(other.preferredHeight),
+ sources(other.sources),
+ db(other.db)
+{
+}
+
+kt::FlagDB::~FlagDB()
+{
+}
+
+void kt::FlagDB::addFlagSource(const FlagDBSource& source)
+{
+ sources.append(source);
+}
+
+void kt::FlagDB::addFlagSource(const char* type, const QString& pathPattern)
+{
+ addFlagSource(FlagDBSource(type, pathPattern));
+}
+
+const QValueList<kt::FlagDBSource>& kt::FlagDB::listSources() const
+{
+ return sources;
+}
+
+bool kt::FlagDB::isFlagAvailable(const QString& country)
+{
+ return getFlag(country).isNull();
+}
+
+const QPixmap& kt::FlagDB::getFlag(const QString& country)
+{
+ const QString& c = country.lower();
+ if (!db.contains(c)) {
+ QImage img;
+ QPixmap pixmap;
+ for (QValueList<FlagDBSource>::const_iterator it = sources.constBegin(); it != sources.constEnd(); it++) {
+ const QString& path = (*it).getPath(c);
+ if (QFile::exists(path)) {
+ if (img.load(path)) {
+ if (img.width() != preferredWidth || img.height() != preferredHeight) {
+ const QImage& imgScaled = img.smoothScale(preferredWidth, preferredHeight, QImage::ScaleMin);
+ if (!imgScaled.isNull()) {
+ pixmap.convertFromImage(imgScaled);
+ break;
+ } else if (img.width() <= preferredWidth || img.height() <= preferredHeight) {
+ pixmap.convertFromImage(img);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ db[c] = (!pixmap.isNull()) ? pixmap : nullPixmap;
+ }
+ return db[c];
+}
diff --git a/plugins/infowidget/flagdb.h b/plugins/infowidget/flagdb.h
new file mode 100644
index 0000000..f2524ab
--- /dev/null
+++ b/plugins/infowidget/flagdb.h
@@ -0,0 +1,68 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Modestas Vainius <modestas@vainius.eu> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef FLAGDB_H
+#define FLAGDB_H
+
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qmap.h>
+
+namespace kt
+{
+ class FlagDBSource
+ {
+ public:
+ FlagDBSource();
+ FlagDBSource(const char* type, const QString& pathPattern);
+ FlagDBSource(const QString& pathPattern);
+ QString getPath(const QString& country) const;
+
+ const char* getType() { return type; };
+ const QString& getPathPattern() { return pathPattern; };
+
+ private:
+ const char* type;
+ const QString pathPattern;
+ };
+
+ /**
+ @author Modestas Vainius
+ */
+ class FlagDB
+ {
+ public :
+ FlagDB(int preferredWidth, int preferredHeight);
+ FlagDB(const FlagDB& m);
+ ~FlagDB();
+
+ void addFlagSource(const FlagDBSource& source);
+ void addFlagSource(const char* type, const QString& pathPattern);
+ const QValueList<FlagDBSource>& listSources() const;
+ bool isFlagAvailable(const QString& country);
+ const QPixmap& getFlag(const QString& country);
+ private:
+ static const QPixmap& nullPixmap;
+ int preferredWidth, preferredHeight;
+ QValueList<FlagDBSource> sources;
+ QMap<QString,QPixmap> db;
+ };
+}
+
+ #endif
diff --git a/plugins/infowidget/floatspinbox.cpp b/plugins/infowidget/floatspinbox.cpp
new file mode 100644
index 0000000..8b78981
--- /dev/null
+++ b/plugins/infowidget/floatspinbox.cpp
@@ -0,0 +1,226 @@
+/***************************************************************************
+ * Copyright (C) 2006 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Vincent Wagelaar <vincent@ricardis.tudelft.nl> *
+ * Jonas Widarsson <jonas@widarsson.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <qlineedit.h>
+#include "floatspinbox.h"
+#include "localefloatvalidator.h"
+
+
+kt::FloatSpinBox::FloatSpinBox(
+ QWidget * parent,
+ const char * name
+ )
+ :QSpinBox( -100, 100, 1, parent, name),
+ m_precision_digits( 2 ),
+ m_value(0.0f),
+ m_minValue(0.0f),
+ m_maxValue(0.0f),
+ m_useRange(false),
+ m_oldIntVal(0)
+{
+ QSpinBox::setValue(0);
+ setStep( 0.25f );
+ connect(this, SIGNAL(valueChanged ( int )), this, SLOT(internalValueChanged( int )));
+ setValidator( new LocaleFloatValidator( dynamic_cast<QSpinBox * > (this) ));
+ editor()->setAlignment(Qt::AlignRight);
+}
+
+kt::FloatSpinBox::FloatSpinBox(
+ int precision,
+ QWidget * parent,
+ const char * name
+ )
+ :QSpinBox( -100, 100, 1, parent, name),
+ m_precision_digits( precision ),
+ m_value(0.0f),
+ m_minValue(0.0f),
+ m_maxValue(0.0f),
+ m_useRange(false),
+ m_oldIntVal(0)
+{
+ QSpinBox::setValue(0);
+ setStep( 0.25 );
+ connect(this, SIGNAL(valueChanged ( int )), this, SLOT(internalValueChanged( int )));
+ setValidator( new LocaleFloatValidator( dynamic_cast<QSpinBox * > (this) ));
+}
+
+
+kt::FloatSpinBox::FloatSpinBox(
+ float minValue,
+ float maxValue,
+ float step,
+ int precision,
+ QWidget * parent,
+ const char * name
+ )
+ :QSpinBox( -100, 100, 1, parent, name),
+ m_precision_digits( precision ),
+ m_value(0.0f),
+ m_minValue(minValue),
+ m_maxValue(maxValue),
+ m_useRange(true),
+ m_oldIntVal(0)
+{
+ QSpinBox::setValue(0);
+ setValue(0.0f);
+ setStep( step );
+ connect(this, SIGNAL(valueChanged ( int )), this, SLOT(internalValueChanged( int )));
+ setValidator( new LocaleFloatValidator( this ));
+
+}
+
+QString kt::FloatSpinBox::mapValueToText( int value )
+{
+ /// This is called from QSpinBox, which passes an int.
+ /// As we don't use the QSpinBox's internal value, we ignore it.
+ QString t = KGlobal::locale()->formatNumber( m_value, m_precision_digits);
+ editor()->setText(t);
+ return t;
+}
+
+int kt::FloatSpinBox::mapTextToValue( bool * ok )
+{
+ /// This is called from QSpinBox, which needs an int for return.
+ /// As we don't use the QSpinBox's internal value, we only return 0.
+
+ float value = KGlobal::locale()->readNumber(text(), ok);
+ if (*ok)
+ {
+ setValue(value);
+ *ok = true;
+ }
+ return 1;
+}
+
+float kt::FloatSpinBox::minValue( ) const
+{
+ return m_minValue;
+}
+
+float kt::FloatSpinBox::maxValue( ) const
+{
+ return m_maxValue;
+}
+
+float kt::FloatSpinBox::value( ) const
+{
+ return m_value;
+}
+
+int kt::FloatSpinBox::precision( ) const
+{
+ return m_precision_digits;
+}
+
+void kt::FloatSpinBox::setMinValue( float minValue )
+{
+ if (minValue <= m_maxValue)
+ {
+ m_minValue = minValue;
+ m_useRange = true;
+ }
+}
+
+void kt::FloatSpinBox::setMaxValue( float maxValue )
+{
+ if (maxValue >= m_minValue)
+ {
+ m_maxValue = maxValue;
+ m_useRange = true;
+ }
+}
+
+void kt::FloatSpinBox::setValue( float value )
+{
+ bool changed = false;
+ if (m_useRange)
+ {
+ float old = m_value;
+ m_value = max(m_minValue, min(m_maxValue, value));
+ if ( old != m_value )
+ changed = true;
+ }
+ else
+ {
+ m_value = value;
+ changed = true;
+ }
+ if (changed)
+ {
+ mapValueToText(0);
+ emit valueChanged( m_value );
+ emit valueHasChanged();
+ }
+}
+
+void kt::FloatSpinBox::setStep( float step)
+{
+ if (step > 0)
+ m_step = step;
+}
+
+void kt::FloatSpinBox::internalValueChanged( int value )
+{
+ /// The step buttons won't work without tracking the
+ /// QSpinbox's value changes.
+
+ if ( value > m_oldIntVal)
+ {
+ stepUp();
+ }
+ else
+ {
+ stepDown();
+ }
+
+ if (value > 10)
+ value -= 20;
+
+ if (value < -10)
+ value += 20;
+
+ m_oldIntVal = value;
+}
+
+void kt::FloatSpinBox::setPrecision( int value )
+{
+ m_precision_digits = value;
+}
+
+void kt::FloatSpinBox::stepUp( )
+{
+ setValue( m_value + m_step );
+}
+
+void kt::FloatSpinBox::stepDown( )
+{
+ setValue( m_value - m_step );
+}
+
+kt::FloatSpinBox::~ FloatSpinBox( )
+{
+}
+
+
+#include "floatspinbox.moc"
diff --git a/plugins/infowidget/floatspinbox.h b/plugins/infowidget/floatspinbox.h
new file mode 100644
index 0000000..42545bc
--- /dev/null
+++ b/plugins/infowidget/floatspinbox.h
@@ -0,0 +1,93 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Vincent Wagelaar <vincent@ricardis.tudelft.nl> *
+ * Jonas Widarsson <jonas@widarsson.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef FLOATSPINBOX_H
+#define FLOATSPINBOX_H
+
+#include <qspinbox.h>
+
+namespace kt
+{
+
+ /**
+ * @author Jonas Widarsson
+ *
+ * A Spinbox for float values which respects KGlobal::locale().
+ * QSpinBox's internal integer value is only
+ * used for step detection.
+ */
+
+ class FloatSpinBox : public QSpinBox
+ {
+ Q_OBJECT
+ public:
+ FloatSpinBox( QWidget* parent=0, const char* name=0 );
+ FloatSpinBox( int precision = 2, QWidget* parent=0, const char* name=0 );
+ FloatSpinBox( float minValue, float maxValue, float step = 0.5, int precision = 2,
+ QWidget* parent=0, const char* name=0 );
+ virtual ~FloatSpinBox();
+
+ QString mapValueToText( int value );
+
+ int mapTextToValue( bool *ok );
+
+ float minValue () const;
+ float maxValue () const;
+ float value () const;
+ int precision() const;
+
+ public slots:
+ virtual void setMinValue ( float minValue );
+ virtual void setMaxValue ( float maxValue );
+ virtual void setValue ( float value );
+ virtual void setStep ( float step );
+ virtual void setPrecision( int value );
+ virtual void stepUp();
+ virtual void stepDown();
+
+ signals:
+ void valueChanged ( float value );
+ void valueHasChanged ();
+
+ private slots:
+ void internalValueChanged( int value );
+
+ private:
+ int m_precision_digits;
+ float m_value;
+ float m_minValue;
+ float m_maxValue;
+ bool m_useRange;
+ float m_step;
+ int m_oldIntVal;
+ float min( float a, float b) const
+ {
+ return (a < b) ? a : b;
+ }
+
+ float max( float a, float b) const
+ {
+ return (a > b) ? a : b;
+ }
+ };
+}
+
+#endif
diff --git a/plugins/infowidget/geoip/FLAGS_LICENCE b/plugins/infowidget/geoip/FLAGS_LICENCE
new file mode 100644
index 0000000..368a8fe
--- /dev/null
+++ b/plugins/infowidget/geoip/FLAGS_LICENCE
@@ -0,0 +1,2 @@
+Flag images found at http://www.hahn-hotel.com/flags/
+"All sets provided by us are free to use to anyone, for commercial or non-commercial websites." \ No newline at end of file
diff --git a/plugins/infowidget/geoip/GeoIP-LICENSE.txt b/plugins/infowidget/geoip/GeoIP-LICENSE.txt
new file mode 100644
index 0000000..457710d
--- /dev/null
+++ b/plugins/infowidget/geoip/GeoIP-LICENSE.txt
@@ -0,0 +1,74 @@
+There are two licenses, one for the C library software, and one for
+the database.
+
+SOFTWARE LICENSE (C library)
+
+The GeoIP C Library is licensed under the GPL. For details see
+the COPYING file.
+
+OPEN DATA LICENSE (GeoIP Standard Edition Database)
+
+Copyright (c) 2003 MaxMind LLC. All Rights Reserved.
+
+All advertising materials and documentation mentioning features or use of
+this database must display the following acknowledgment:
+"This product includes GeoIP data created by MaxMind, available from
+http://maxmind.com/"
+
+Redistribution and use with or without modification, are permitted provided
+that the following conditions are met:
+1. Redistributions must retain the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other
+materials provided with the distribution.
+2. All advertising materials and documentation mentioning features or use of
+this database must display the following acknowledgement:
+"This product includes GeoIP data created by MaxMind, available from
+http://maxmind.com/"
+3. "MaxMind" may not be used to endorse or promote products derived from this
+database without specific prior written permission.
+
+THIS DATABASE IS PROVIDED BY MAXMIND.COM ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL MAXMIND.COM BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+DATABASE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Some parts of this software distribution are derived from the APNIC, ARIN and
+RIPE databases (copyright details below). The author of this module makes no
+claims of ownership on those parts.
+
+APNIC conditions of use:
+
+The files are freely available for download and use on the condition that APNIC
+will not be held responsible for any loss or damage arising from the application
+of the information contained in these reports.
+
+APNIC endeavours to the best of its ability to ensure the accuracy of these
+reports; however, APNIC makes no guarantee in this regard.
+
+In particular, it should be noted that these reports seek to indicate the
+country where resources were first allocated or assigned. It is not intended
+that these reports be considered as an authoritative statement of the location
+in which any specific resource may currently be in use.
+
+ARIN database copyright:
+
+Copyright (c) American Registry for Internet Numbers. All rights reserved.
+
+RIPE database copyright:
+
+The information in the RIPE Database is available to the public for agreed
+Internet operation purposes, but is under copyright. The copyright statement is:
+
+"Except for agreed Internet operational purposes, no part of this publication
+may be reproduced, stored in a retrieval system, or transmitted, in any form or
+by any means, electronic, mechanical, recording, or otherwise, without prior
+permission of the RIPE NCC on behalf of the copyright holders. Any use of this
+material to target advertising or similar activities is explicitly forbidden and
+may be prosecuted. The RIPE NCC requests to be notified of any such activities
+or suspicions thereof."
diff --git a/plugins/infowidget/geoip/Makefile.am b/plugins/infowidget/geoip/Makefile.am
new file mode 100644
index 0000000..1a7d6c4
--- /dev/null
+++ b/plugins/infowidget/geoip/Makefile.am
@@ -0,0 +1,41 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+
+ktdatadir = $(kde_datadir)/ktorrent/geoip
+
+if ENABLE_GEOIP
+GEOIP_FILES = geoip.dat GeoIP-LICENSE.txt
+else
+GEOIP_FILES =
+endif
+
+if ENABLE_BUILTIN_COUNTRY_FLAGS
+FLAG_FILES = ad.png ae.png af.png ag.png ai.png al.png am.png an.png ao.png \
+ aq.png ar.png as.png at.png au.png aw.png az.png ba.png bb.png bd.png be.png \
+ bf.png bg.png bh.png bi.png bj.png bm.png bn.png bo.png br.png bs.png bt.png \
+ bv.png bw.png by.png bz.png ca.png cc.png cd.png cf.png cg.png ch.png ci.png \
+ ck.png cl.png cm.png cn.png co.png cr.png cs.png cu.png cv.png cx.png cy.png \
+ cz.png de.png dj.png dk.png dm.png do.png dz.png ec.png ee.png eg.png eh.png \
+ er.png es.png et.png eu.png fi.png fj.png fk.png fm.png fo.png fr.png ga.png \
+ gb.png gd.png ge.png gf.png gh.png gi.png gl.png gm.png gn.png gp.png gq.png \
+ gr.png gs.png gt.png gu.png gw.png gy.png hk.png hm.png hn.png hr.png ht.png \
+ hu.png id.png ie.png il.png in.png io.png iq.png ir.png is.png it.png jm.png \
+ jo.png jp.png ke.png kg.png kh.png ki.png km.png kn.png kp.png kr.png kw.png \
+ ky.png kz.png la.png lb.png lc.png li.png lk.png lr.png ls.png lt.png lu.png \
+ lv.png ly.png ma.png mc.png md.png mg.png mh.png mk.png ml.png mm.png mn.png \
+ mo.png mp.png mq.png mr.png ms.png mt.png mu.png mv.png mw.png mx.png my.png \
+ mz.png na.png nc.png ne.png nf.png ng.png ni.png nl.png no.png np.png nr.png \
+ nu.png nz.png om.png pa.png pe.png pf.png pg.png ph.png pk.png pl.png pm.png \
+ pn.png pr.png ps.png pt.png pw.png py.png qa.png re.png ro.png ru.png rw.png \
+ sa.png sb.png sc.png sd.png se.png sg.png sh.png si.png sj.png sk.png sl.png \
+ sm.png sn.png so.png sr.png st.png sv.png sy.png sz.png tc.png td.png tf.png \
+ tg.png th.png tj.png tk.png tl.png tm.png tn.png to.png tp.png tr.png tt.png \
+ tv.png tw.png tz.png ua.png ug.png um.png us.png uy.png uz.png va.png vc.png \
+ ve.png vg.png vi.png vn.png vu.png wf.png ws.png ye.png yt.png yu.png za.png \
+ zm.png zw.png FLAGS_LICENCE
+else
+FLAG_FILES =
+endif
+
+ktdata_DATA = $(FLAG_FILES) $(GEOIP_FILES)
+
diff --git a/plugins/infowidget/geoip/ad.png b/plugins/infowidget/geoip/ad.png
new file mode 100644
index 0000000..bcf1e35
--- /dev/null
+++ b/plugins/infowidget/geoip/ad.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ae.png b/plugins/infowidget/geoip/ae.png
new file mode 100644
index 0000000..4ce8134
--- /dev/null
+++ b/plugins/infowidget/geoip/ae.png
Binary files differ
diff --git a/plugins/infowidget/geoip/af.png b/plugins/infowidget/geoip/af.png
new file mode 100644
index 0000000..14027c0
--- /dev/null
+++ b/plugins/infowidget/geoip/af.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ag.png b/plugins/infowidget/geoip/ag.png
new file mode 100644
index 0000000..93b3eb8
--- /dev/null
+++ b/plugins/infowidget/geoip/ag.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ai.png b/plugins/infowidget/geoip/ai.png
new file mode 100644
index 0000000..6aa3b55
--- /dev/null
+++ b/plugins/infowidget/geoip/ai.png
Binary files differ
diff --git a/plugins/infowidget/geoip/al.png b/plugins/infowidget/geoip/al.png
new file mode 100644
index 0000000..04f7c13
--- /dev/null
+++ b/plugins/infowidget/geoip/al.png
Binary files differ
diff --git a/plugins/infowidget/geoip/am.png b/plugins/infowidget/geoip/am.png
new file mode 100644
index 0000000..dbecf96
--- /dev/null
+++ b/plugins/infowidget/geoip/am.png
Binary files differ
diff --git a/plugins/infowidget/geoip/an.png b/plugins/infowidget/geoip/an.png
new file mode 100644
index 0000000..974daba
--- /dev/null
+++ b/plugins/infowidget/geoip/an.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ao.png b/plugins/infowidget/geoip/ao.png
new file mode 100644
index 0000000..5e08932
--- /dev/null
+++ b/plugins/infowidget/geoip/ao.png
Binary files differ
diff --git a/plugins/infowidget/geoip/aq.png b/plugins/infowidget/geoip/aq.png
new file mode 100644
index 0000000..b08a8b6
--- /dev/null
+++ b/plugins/infowidget/geoip/aq.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ar.png b/plugins/infowidget/geoip/ar.png
new file mode 100644
index 0000000..68c99d9
--- /dev/null
+++ b/plugins/infowidget/geoip/ar.png
Binary files differ
diff --git a/plugins/infowidget/geoip/as.png b/plugins/infowidget/geoip/as.png
new file mode 100644
index 0000000..5903f6c
--- /dev/null
+++ b/plugins/infowidget/geoip/as.png
Binary files differ
diff --git a/plugins/infowidget/geoip/at.png b/plugins/infowidget/geoip/at.png
new file mode 100644
index 0000000..1570d7a
--- /dev/null
+++ b/plugins/infowidget/geoip/at.png
Binary files differ
diff --git a/plugins/infowidget/geoip/au.png b/plugins/infowidget/geoip/au.png
new file mode 100644
index 0000000..486646b
--- /dev/null
+++ b/plugins/infowidget/geoip/au.png
Binary files differ
diff --git a/plugins/infowidget/geoip/aw.png b/plugins/infowidget/geoip/aw.png
new file mode 100644
index 0000000..5831bbe
--- /dev/null
+++ b/plugins/infowidget/geoip/aw.png
Binary files differ
diff --git a/plugins/infowidget/geoip/az.png b/plugins/infowidget/geoip/az.png
new file mode 100644
index 0000000..420a871
--- /dev/null
+++ b/plugins/infowidget/geoip/az.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ba.png b/plugins/infowidget/geoip/ba.png
new file mode 100644
index 0000000..dff9671
--- /dev/null
+++ b/plugins/infowidget/geoip/ba.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bb.png b/plugins/infowidget/geoip/bb.png
new file mode 100644
index 0000000..1811232
--- /dev/null
+++ b/plugins/infowidget/geoip/bb.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bd.png b/plugins/infowidget/geoip/bd.png
new file mode 100644
index 0000000..034f07a
--- /dev/null
+++ b/plugins/infowidget/geoip/bd.png
Binary files differ
diff --git a/plugins/infowidget/geoip/be.png b/plugins/infowidget/geoip/be.png
new file mode 100644
index 0000000..4b50190
--- /dev/null
+++ b/plugins/infowidget/geoip/be.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bf.png b/plugins/infowidget/geoip/bf.png
new file mode 100644
index 0000000..4b522d8
--- /dev/null
+++ b/plugins/infowidget/geoip/bf.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bg.png b/plugins/infowidget/geoip/bg.png
new file mode 100644
index 0000000..60aae1d
--- /dev/null
+++ b/plugins/infowidget/geoip/bg.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bh.png b/plugins/infowidget/geoip/bh.png
new file mode 100644
index 0000000..b6524c2
--- /dev/null
+++ b/plugins/infowidget/geoip/bh.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bi.png b/plugins/infowidget/geoip/bi.png
new file mode 100644
index 0000000..d4751e0
--- /dev/null
+++ b/plugins/infowidget/geoip/bi.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bj.png b/plugins/infowidget/geoip/bj.png
new file mode 100644
index 0000000..4dbfd3f
--- /dev/null
+++ b/plugins/infowidget/geoip/bj.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bm.png b/plugins/infowidget/geoip/bm.png
new file mode 100644
index 0000000..c178fe7
--- /dev/null
+++ b/plugins/infowidget/geoip/bm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bn.png b/plugins/infowidget/geoip/bn.png
new file mode 100644
index 0000000..291fc80
--- /dev/null
+++ b/plugins/infowidget/geoip/bn.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bo.png b/plugins/infowidget/geoip/bo.png
new file mode 100644
index 0000000..ad2120b
--- /dev/null
+++ b/plugins/infowidget/geoip/bo.png
Binary files differ
diff --git a/plugins/infowidget/geoip/br.png b/plugins/infowidget/geoip/br.png
new file mode 100644
index 0000000..bd1b880
--- /dev/null
+++ b/plugins/infowidget/geoip/br.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bs.png b/plugins/infowidget/geoip/bs.png
new file mode 100644
index 0000000..5395236
--- /dev/null
+++ b/plugins/infowidget/geoip/bs.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bt.png b/plugins/infowidget/geoip/bt.png
new file mode 100644
index 0000000..ef1d430
--- /dev/null
+++ b/plugins/infowidget/geoip/bt.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bv.png b/plugins/infowidget/geoip/bv.png
new file mode 100644
index 0000000..ed4c76b
--- /dev/null
+++ b/plugins/infowidget/geoip/bv.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bw.png b/plugins/infowidget/geoip/bw.png
new file mode 100644
index 0000000..c69641b
--- /dev/null
+++ b/plugins/infowidget/geoip/bw.png
Binary files differ
diff --git a/plugins/infowidget/geoip/by.png b/plugins/infowidget/geoip/by.png
new file mode 100644
index 0000000..655f0ae
--- /dev/null
+++ b/plugins/infowidget/geoip/by.png
Binary files differ
diff --git a/plugins/infowidget/geoip/bz.png b/plugins/infowidget/geoip/bz.png
new file mode 100644
index 0000000..92964f8
--- /dev/null
+++ b/plugins/infowidget/geoip/bz.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ca.png b/plugins/infowidget/geoip/ca.png
new file mode 100644
index 0000000..757ea92
--- /dev/null
+++ b/plugins/infowidget/geoip/ca.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cc.png b/plugins/infowidget/geoip/cc.png
new file mode 100644
index 0000000..ba02461
--- /dev/null
+++ b/plugins/infowidget/geoip/cc.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cd.png b/plugins/infowidget/geoip/cd.png
new file mode 100644
index 0000000..6d6e96c
--- /dev/null
+++ b/plugins/infowidget/geoip/cd.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cf.png b/plugins/infowidget/geoip/cf.png
new file mode 100644
index 0000000..fa2b560
--- /dev/null
+++ b/plugins/infowidget/geoip/cf.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cg.png b/plugins/infowidget/geoip/cg.png
new file mode 100644
index 0000000..aae69bb
--- /dev/null
+++ b/plugins/infowidget/geoip/cg.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ch.png b/plugins/infowidget/geoip/ch.png
new file mode 100644
index 0000000..aa2e8cd
--- /dev/null
+++ b/plugins/infowidget/geoip/ch.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ci.png b/plugins/infowidget/geoip/ci.png
new file mode 100644
index 0000000..b06b6e1
--- /dev/null
+++ b/plugins/infowidget/geoip/ci.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ck.png b/plugins/infowidget/geoip/ck.png
new file mode 100644
index 0000000..b70d582
--- /dev/null
+++ b/plugins/infowidget/geoip/ck.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cl.png b/plugins/infowidget/geoip/cl.png
new file mode 100644
index 0000000..148f2bc
--- /dev/null
+++ b/plugins/infowidget/geoip/cl.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cm.png b/plugins/infowidget/geoip/cm.png
new file mode 100644
index 0000000..f47d808
--- /dev/null
+++ b/plugins/infowidget/geoip/cm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cn.png b/plugins/infowidget/geoip/cn.png
new file mode 100644
index 0000000..f6c4a20
--- /dev/null
+++ b/plugins/infowidget/geoip/cn.png
Binary files differ
diff --git a/plugins/infowidget/geoip/co.png b/plugins/infowidget/geoip/co.png
new file mode 100644
index 0000000..b6636cd
--- /dev/null
+++ b/plugins/infowidget/geoip/co.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cr.png b/plugins/infowidget/geoip/cr.png
new file mode 100644
index 0000000..f4c0d03
--- /dev/null
+++ b/plugins/infowidget/geoip/cr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cs.png b/plugins/infowidget/geoip/cs.png
new file mode 100644
index 0000000..f923c01
--- /dev/null
+++ b/plugins/infowidget/geoip/cs.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cu.png b/plugins/infowidget/geoip/cu.png
new file mode 100644
index 0000000..edc0752
--- /dev/null
+++ b/plugins/infowidget/geoip/cu.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cv.png b/plugins/infowidget/geoip/cv.png
new file mode 100644
index 0000000..d6d1a76
--- /dev/null
+++ b/plugins/infowidget/geoip/cv.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cx.png b/plugins/infowidget/geoip/cx.png
new file mode 100644
index 0000000..452b405
--- /dev/null
+++ b/plugins/infowidget/geoip/cx.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cy.png b/plugins/infowidget/geoip/cy.png
new file mode 100644
index 0000000..6d6d36a
--- /dev/null
+++ b/plugins/infowidget/geoip/cy.png
Binary files differ
diff --git a/plugins/infowidget/geoip/cz.png b/plugins/infowidget/geoip/cz.png
new file mode 100644
index 0000000..1aaca40
--- /dev/null
+++ b/plugins/infowidget/geoip/cz.png
Binary files differ
diff --git a/plugins/infowidget/geoip/de.png b/plugins/infowidget/geoip/de.png
new file mode 100644
index 0000000..fbbc44f
--- /dev/null
+++ b/plugins/infowidget/geoip/de.png
Binary files differ
diff --git a/plugins/infowidget/geoip/dj.png b/plugins/infowidget/geoip/dj.png
new file mode 100644
index 0000000..95d4e30
--- /dev/null
+++ b/plugins/infowidget/geoip/dj.png
Binary files differ
diff --git a/plugins/infowidget/geoip/dk.png b/plugins/infowidget/geoip/dk.png
new file mode 100644
index 0000000..6f60a4a
--- /dev/null
+++ b/plugins/infowidget/geoip/dk.png
Binary files differ
diff --git a/plugins/infowidget/geoip/dm.png b/plugins/infowidget/geoip/dm.png
new file mode 100644
index 0000000..7f240cf
--- /dev/null
+++ b/plugins/infowidget/geoip/dm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/do.png b/plugins/infowidget/geoip/do.png
new file mode 100644
index 0000000..78360ad
--- /dev/null
+++ b/plugins/infowidget/geoip/do.png
Binary files differ
diff --git a/plugins/infowidget/geoip/dz.png b/plugins/infowidget/geoip/dz.png
new file mode 100644
index 0000000..8a53c6f
--- /dev/null
+++ b/plugins/infowidget/geoip/dz.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ec.png b/plugins/infowidget/geoip/ec.png
new file mode 100644
index 0000000..aaac909
--- /dev/null
+++ b/plugins/infowidget/geoip/ec.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ee.png b/plugins/infowidget/geoip/ee.png
new file mode 100644
index 0000000..6258c3b
--- /dev/null
+++ b/plugins/infowidget/geoip/ee.png
Binary files differ
diff --git a/plugins/infowidget/geoip/eg.png b/plugins/infowidget/geoip/eg.png
new file mode 100644
index 0000000..8725af0
--- /dev/null
+++ b/plugins/infowidget/geoip/eg.png
Binary files differ
diff --git a/plugins/infowidget/geoip/eh.png b/plugins/infowidget/geoip/eh.png
new file mode 100644
index 0000000..e142882
--- /dev/null
+++ b/plugins/infowidget/geoip/eh.png
Binary files differ
diff --git a/plugins/infowidget/geoip/er.png b/plugins/infowidget/geoip/er.png
new file mode 100644
index 0000000..dbd752c
--- /dev/null
+++ b/plugins/infowidget/geoip/er.png
Binary files differ
diff --git a/plugins/infowidget/geoip/es.png b/plugins/infowidget/geoip/es.png
new file mode 100644
index 0000000..dd4d184
--- /dev/null
+++ b/plugins/infowidget/geoip/es.png
Binary files differ
diff --git a/plugins/infowidget/geoip/et.png b/plugins/infowidget/geoip/et.png
new file mode 100644
index 0000000..e28bd0b
--- /dev/null
+++ b/plugins/infowidget/geoip/et.png
Binary files differ
diff --git a/plugins/infowidget/geoip/eu.png b/plugins/infowidget/geoip/eu.png
new file mode 100644
index 0000000..d25bdca
--- /dev/null
+++ b/plugins/infowidget/geoip/eu.png
Binary files differ
diff --git a/plugins/infowidget/geoip/fi.png b/plugins/infowidget/geoip/fi.png
new file mode 100644
index 0000000..a198c11
--- /dev/null
+++ b/plugins/infowidget/geoip/fi.png
Binary files differ
diff --git a/plugins/infowidget/geoip/fj.png b/plugins/infowidget/geoip/fj.png
new file mode 100644
index 0000000..d3c3126
--- /dev/null
+++ b/plugins/infowidget/geoip/fj.png
Binary files differ
diff --git a/plugins/infowidget/geoip/fk.png b/plugins/infowidget/geoip/fk.png
new file mode 100644
index 0000000..7141415
--- /dev/null
+++ b/plugins/infowidget/geoip/fk.png
Binary files differ
diff --git a/plugins/infowidget/geoip/fm.png b/plugins/infowidget/geoip/fm.png
new file mode 100644
index 0000000..05b7f0c
--- /dev/null
+++ b/plugins/infowidget/geoip/fm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/fo.png b/plugins/infowidget/geoip/fo.png
new file mode 100644
index 0000000..79311ec
--- /dev/null
+++ b/plugins/infowidget/geoip/fo.png
Binary files differ
diff --git a/plugins/infowidget/geoip/fr.png b/plugins/infowidget/geoip/fr.png
new file mode 100644
index 0000000..348ffa5
--- /dev/null
+++ b/plugins/infowidget/geoip/fr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ga.png b/plugins/infowidget/geoip/ga.png
new file mode 100644
index 0000000..643093f
--- /dev/null
+++ b/plugins/infowidget/geoip/ga.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gb.png b/plugins/infowidget/geoip/gb.png
new file mode 100644
index 0000000..45c8bc7
--- /dev/null
+++ b/plugins/infowidget/geoip/gb.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gd.png b/plugins/infowidget/geoip/gd.png
new file mode 100644
index 0000000..ad838b9
--- /dev/null
+++ b/plugins/infowidget/geoip/gd.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ge.png b/plugins/infowidget/geoip/ge.png
new file mode 100644
index 0000000..75d7c88
--- /dev/null
+++ b/plugins/infowidget/geoip/ge.png
Binary files differ
diff --git a/plugins/infowidget/geoip/geoip.dat b/plugins/infowidget/geoip/geoip.dat
new file mode 100644
index 0000000..11f1f6b
--- /dev/null
+++ b/plugins/infowidget/geoip/geoip.dat
Binary files differ
diff --git a/plugins/infowidget/geoip/gf.png b/plugins/infowidget/geoip/gf.png
new file mode 100644
index 0000000..37f4b07
--- /dev/null
+++ b/plugins/infowidget/geoip/gf.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gh.png b/plugins/infowidget/geoip/gh.png
new file mode 100644
index 0000000..798bc00
--- /dev/null
+++ b/plugins/infowidget/geoip/gh.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gi.png b/plugins/infowidget/geoip/gi.png
new file mode 100644
index 0000000..2839ba1
--- /dev/null
+++ b/plugins/infowidget/geoip/gi.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gl.png b/plugins/infowidget/geoip/gl.png
new file mode 100644
index 0000000..0690b6f
--- /dev/null
+++ b/plugins/infowidget/geoip/gl.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gm.png b/plugins/infowidget/geoip/gm.png
new file mode 100644
index 0000000..8bcb283
--- /dev/null
+++ b/plugins/infowidget/geoip/gm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gn.png b/plugins/infowidget/geoip/gn.png
new file mode 100644
index 0000000..48ee312
--- /dev/null
+++ b/plugins/infowidget/geoip/gn.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gp.png b/plugins/infowidget/geoip/gp.png
new file mode 100644
index 0000000..e627bf2
--- /dev/null
+++ b/plugins/infowidget/geoip/gp.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gq.png b/plugins/infowidget/geoip/gq.png
new file mode 100644
index 0000000..4b9a1be
--- /dev/null
+++ b/plugins/infowidget/geoip/gq.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gr.png b/plugins/infowidget/geoip/gr.png
new file mode 100644
index 0000000..ce31c8b
--- /dev/null
+++ b/plugins/infowidget/geoip/gr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gs.png b/plugins/infowidget/geoip/gs.png
new file mode 100644
index 0000000..c0f3d5a
--- /dev/null
+++ b/plugins/infowidget/geoip/gs.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gt.png b/plugins/infowidget/geoip/gt.png
new file mode 100644
index 0000000..ed7113f
--- /dev/null
+++ b/plugins/infowidget/geoip/gt.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gu.png b/plugins/infowidget/geoip/gu.png
new file mode 100644
index 0000000..b59cb44
--- /dev/null
+++ b/plugins/infowidget/geoip/gu.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gw.png b/plugins/infowidget/geoip/gw.png
new file mode 100644
index 0000000..075c5e2
--- /dev/null
+++ b/plugins/infowidget/geoip/gw.png
Binary files differ
diff --git a/plugins/infowidget/geoip/gy.png b/plugins/infowidget/geoip/gy.png
new file mode 100644
index 0000000..fc64031
--- /dev/null
+++ b/plugins/infowidget/geoip/gy.png
Binary files differ
diff --git a/plugins/infowidget/geoip/hk.png b/plugins/infowidget/geoip/hk.png
new file mode 100644
index 0000000..c2baf31
--- /dev/null
+++ b/plugins/infowidget/geoip/hk.png
Binary files differ
diff --git a/plugins/infowidget/geoip/hm.png b/plugins/infowidget/geoip/hm.png
new file mode 100644
index 0000000..ff84e0b
--- /dev/null
+++ b/plugins/infowidget/geoip/hm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/hn.png b/plugins/infowidget/geoip/hn.png
new file mode 100644
index 0000000..ff8d0ba
--- /dev/null
+++ b/plugins/infowidget/geoip/hn.png
Binary files differ
diff --git a/plugins/infowidget/geoip/hr.png b/plugins/infowidget/geoip/hr.png
new file mode 100644
index 0000000..4965c41
--- /dev/null
+++ b/plugins/infowidget/geoip/hr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ht.png b/plugins/infowidget/geoip/ht.png
new file mode 100644
index 0000000..81541e3
--- /dev/null
+++ b/plugins/infowidget/geoip/ht.png
Binary files differ
diff --git a/plugins/infowidget/geoip/hu.png b/plugins/infowidget/geoip/hu.png
new file mode 100644
index 0000000..48afe0e
--- /dev/null
+++ b/plugins/infowidget/geoip/hu.png
Binary files differ
diff --git a/plugins/infowidget/geoip/id.png b/plugins/infowidget/geoip/id.png
new file mode 100644
index 0000000..b824f31
--- /dev/null
+++ b/plugins/infowidget/geoip/id.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ie.png b/plugins/infowidget/geoip/ie.png
new file mode 100644
index 0000000..8f881c8
--- /dev/null
+++ b/plugins/infowidget/geoip/ie.png
Binary files differ
diff --git a/plugins/infowidget/geoip/il.png b/plugins/infowidget/geoip/il.png
new file mode 100644
index 0000000..68e273c
--- /dev/null
+++ b/plugins/infowidget/geoip/il.png
Binary files differ
diff --git a/plugins/infowidget/geoip/in.png b/plugins/infowidget/geoip/in.png
new file mode 100644
index 0000000..34abb74
--- /dev/null
+++ b/plugins/infowidget/geoip/in.png
Binary files differ
diff --git a/plugins/infowidget/geoip/io.png b/plugins/infowidget/geoip/io.png
new file mode 100644
index 0000000..f5dd57e
--- /dev/null
+++ b/plugins/infowidget/geoip/io.png
Binary files differ
diff --git a/plugins/infowidget/geoip/iq.png b/plugins/infowidget/geoip/iq.png
new file mode 100644
index 0000000..a457f80
--- /dev/null
+++ b/plugins/infowidget/geoip/iq.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ir.png b/plugins/infowidget/geoip/ir.png
new file mode 100644
index 0000000..eac13ef
--- /dev/null
+++ b/plugins/infowidget/geoip/ir.png
Binary files differ
diff --git a/plugins/infowidget/geoip/is.png b/plugins/infowidget/geoip/is.png
new file mode 100644
index 0000000..2089318
--- /dev/null
+++ b/plugins/infowidget/geoip/is.png
Binary files differ
diff --git a/plugins/infowidget/geoip/it.png b/plugins/infowidget/geoip/it.png
new file mode 100644
index 0000000..09836d1
--- /dev/null
+++ b/plugins/infowidget/geoip/it.png
Binary files differ
diff --git a/plugins/infowidget/geoip/jm.png b/plugins/infowidget/geoip/jm.png
new file mode 100644
index 0000000..7fdfb37
--- /dev/null
+++ b/plugins/infowidget/geoip/jm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/jo.png b/plugins/infowidget/geoip/jo.png
new file mode 100644
index 0000000..2b5077f
--- /dev/null
+++ b/plugins/infowidget/geoip/jo.png
Binary files differ
diff --git a/plugins/infowidget/geoip/jp.png b/plugins/infowidget/geoip/jp.png
new file mode 100644
index 0000000..98cd271
--- /dev/null
+++ b/plugins/infowidget/geoip/jp.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ke.png b/plugins/infowidget/geoip/ke.png
new file mode 100644
index 0000000..8192eb8
--- /dev/null
+++ b/plugins/infowidget/geoip/ke.png
Binary files differ
diff --git a/plugins/infowidget/geoip/kg.png b/plugins/infowidget/geoip/kg.png
new file mode 100644
index 0000000..846e83f
--- /dev/null
+++ b/plugins/infowidget/geoip/kg.png
Binary files differ
diff --git a/plugins/infowidget/geoip/kh.png b/plugins/infowidget/geoip/kh.png
new file mode 100644
index 0000000..e8b4481
--- /dev/null
+++ b/plugins/infowidget/geoip/kh.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ki.png b/plugins/infowidget/geoip/ki.png
new file mode 100644
index 0000000..b6e05c6
--- /dev/null
+++ b/plugins/infowidget/geoip/ki.png
Binary files differ
diff --git a/plugins/infowidget/geoip/km.png b/plugins/infowidget/geoip/km.png
new file mode 100644
index 0000000..cf3823a
--- /dev/null
+++ b/plugins/infowidget/geoip/km.png
Binary files differ
diff --git a/plugins/infowidget/geoip/kn.png b/plugins/infowidget/geoip/kn.png
new file mode 100644
index 0000000..82320ae
--- /dev/null
+++ b/plugins/infowidget/geoip/kn.png
Binary files differ
diff --git a/plugins/infowidget/geoip/kp.png b/plugins/infowidget/geoip/kp.png
new file mode 100644
index 0000000..7d86c9f
--- /dev/null
+++ b/plugins/infowidget/geoip/kp.png
Binary files differ
diff --git a/plugins/infowidget/geoip/kr.png b/plugins/infowidget/geoip/kr.png
new file mode 100644
index 0000000..93231a8
--- /dev/null
+++ b/plugins/infowidget/geoip/kr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/kw.png b/plugins/infowidget/geoip/kw.png
new file mode 100644
index 0000000..11a64ba
--- /dev/null
+++ b/plugins/infowidget/geoip/kw.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ky.png b/plugins/infowidget/geoip/ky.png
new file mode 100644
index 0000000..94ec427
--- /dev/null
+++ b/plugins/infowidget/geoip/ky.png
Binary files differ
diff --git a/plugins/infowidget/geoip/kz.png b/plugins/infowidget/geoip/kz.png
new file mode 100644
index 0000000..d2061c3
--- /dev/null
+++ b/plugins/infowidget/geoip/kz.png
Binary files differ
diff --git a/plugins/infowidget/geoip/la.png b/plugins/infowidget/geoip/la.png
new file mode 100644
index 0000000..d15f089
--- /dev/null
+++ b/plugins/infowidget/geoip/la.png
Binary files differ
diff --git a/plugins/infowidget/geoip/lb.png b/plugins/infowidget/geoip/lb.png
new file mode 100644
index 0000000..c560362
--- /dev/null
+++ b/plugins/infowidget/geoip/lb.png
Binary files differ
diff --git a/plugins/infowidget/geoip/lc.png b/plugins/infowidget/geoip/lc.png
new file mode 100644
index 0000000..bdbe4ea
--- /dev/null
+++ b/plugins/infowidget/geoip/lc.png
Binary files differ
diff --git a/plugins/infowidget/geoip/li.png b/plugins/infowidget/geoip/li.png
new file mode 100644
index 0000000..82ed982
--- /dev/null
+++ b/plugins/infowidget/geoip/li.png
Binary files differ
diff --git a/plugins/infowidget/geoip/lk.png b/plugins/infowidget/geoip/lk.png
new file mode 100644
index 0000000..ea3ec46
--- /dev/null
+++ b/plugins/infowidget/geoip/lk.png
Binary files differ
diff --git a/plugins/infowidget/geoip/lr.png b/plugins/infowidget/geoip/lr.png
new file mode 100644
index 0000000..bc55adf
--- /dev/null
+++ b/plugins/infowidget/geoip/lr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ls.png b/plugins/infowidget/geoip/ls.png
new file mode 100644
index 0000000..79cd5ad
--- /dev/null
+++ b/plugins/infowidget/geoip/ls.png
Binary files differ
diff --git a/plugins/infowidget/geoip/lt.png b/plugins/infowidget/geoip/lt.png
new file mode 100644
index 0000000..d865e39
--- /dev/null
+++ b/plugins/infowidget/geoip/lt.png
Binary files differ
diff --git a/plugins/infowidget/geoip/lu.png b/plugins/infowidget/geoip/lu.png
new file mode 100644
index 0000000..740a8f7
--- /dev/null
+++ b/plugins/infowidget/geoip/lu.png
Binary files differ
diff --git a/plugins/infowidget/geoip/lv.png b/plugins/infowidget/geoip/lv.png
new file mode 100644
index 0000000..d59a1fc
--- /dev/null
+++ b/plugins/infowidget/geoip/lv.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ly.png b/plugins/infowidget/geoip/ly.png
new file mode 100644
index 0000000..5cb02fb
--- /dev/null
+++ b/plugins/infowidget/geoip/ly.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ma.png b/plugins/infowidget/geoip/ma.png
new file mode 100644
index 0000000..2737e19
--- /dev/null
+++ b/plugins/infowidget/geoip/ma.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mc.png b/plugins/infowidget/geoip/mc.png
new file mode 100644
index 0000000..cfa1b92
--- /dev/null
+++ b/plugins/infowidget/geoip/mc.png
Binary files differ
diff --git a/plugins/infowidget/geoip/md.png b/plugins/infowidget/geoip/md.png
new file mode 100644
index 0000000..b88c574
--- /dev/null
+++ b/plugins/infowidget/geoip/md.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mg.png b/plugins/infowidget/geoip/mg.png
new file mode 100644
index 0000000..33d38af
--- /dev/null
+++ b/plugins/infowidget/geoip/mg.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mh.png b/plugins/infowidget/geoip/mh.png
new file mode 100644
index 0000000..344d3ae
--- /dev/null
+++ b/plugins/infowidget/geoip/mh.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mk.png b/plugins/infowidget/geoip/mk.png
new file mode 100644
index 0000000..af3cd2c
--- /dev/null
+++ b/plugins/infowidget/geoip/mk.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ml.png b/plugins/infowidget/geoip/ml.png
new file mode 100644
index 0000000..bbc8c61
--- /dev/null
+++ b/plugins/infowidget/geoip/ml.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mm.png b/plugins/infowidget/geoip/mm.png
new file mode 100644
index 0000000..f1c021a
--- /dev/null
+++ b/plugins/infowidget/geoip/mm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mn.png b/plugins/infowidget/geoip/mn.png
new file mode 100644
index 0000000..5662c2e
--- /dev/null
+++ b/plugins/infowidget/geoip/mn.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mo.png b/plugins/infowidget/geoip/mo.png
new file mode 100644
index 0000000..68bdd10
--- /dev/null
+++ b/plugins/infowidget/geoip/mo.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mp.png b/plugins/infowidget/geoip/mp.png
new file mode 100644
index 0000000..cec5b93
--- /dev/null
+++ b/plugins/infowidget/geoip/mp.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mq.png b/plugins/infowidget/geoip/mq.png
new file mode 100644
index 0000000..6704e60
--- /dev/null
+++ b/plugins/infowidget/geoip/mq.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mr.png b/plugins/infowidget/geoip/mr.png
new file mode 100644
index 0000000..7b37d98
--- /dev/null
+++ b/plugins/infowidget/geoip/mr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ms.png b/plugins/infowidget/geoip/ms.png
new file mode 100644
index 0000000..487b59b
--- /dev/null
+++ b/plugins/infowidget/geoip/ms.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mt.png b/plugins/infowidget/geoip/mt.png
new file mode 100644
index 0000000..2cd7bb9
--- /dev/null
+++ b/plugins/infowidget/geoip/mt.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mu.png b/plugins/infowidget/geoip/mu.png
new file mode 100644
index 0000000..d57f15e
--- /dev/null
+++ b/plugins/infowidget/geoip/mu.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mv.png b/plugins/infowidget/geoip/mv.png
new file mode 100644
index 0000000..4972a4c
--- /dev/null
+++ b/plugins/infowidget/geoip/mv.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mw.png b/plugins/infowidget/geoip/mw.png
new file mode 100644
index 0000000..9086528
--- /dev/null
+++ b/plugins/infowidget/geoip/mw.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mx.png b/plugins/infowidget/geoip/mx.png
new file mode 100644
index 0000000..c68a926
--- /dev/null
+++ b/plugins/infowidget/geoip/mx.png
Binary files differ
diff --git a/plugins/infowidget/geoip/my.png b/plugins/infowidget/geoip/my.png
new file mode 100644
index 0000000..2989d2e
--- /dev/null
+++ b/plugins/infowidget/geoip/my.png
Binary files differ
diff --git a/plugins/infowidget/geoip/mz.png b/plugins/infowidget/geoip/mz.png
new file mode 100644
index 0000000..7281b03
--- /dev/null
+++ b/plugins/infowidget/geoip/mz.png
Binary files differ
diff --git a/plugins/infowidget/geoip/na.png b/plugins/infowidget/geoip/na.png
new file mode 100644
index 0000000..258c942
--- /dev/null
+++ b/plugins/infowidget/geoip/na.png
Binary files differ
diff --git a/plugins/infowidget/geoip/nc.png b/plugins/infowidget/geoip/nc.png
new file mode 100644
index 0000000..2abf002
--- /dev/null
+++ b/plugins/infowidget/geoip/nc.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ne.png b/plugins/infowidget/geoip/ne.png
new file mode 100644
index 0000000..d0c59a1
--- /dev/null
+++ b/plugins/infowidget/geoip/ne.png
Binary files differ
diff --git a/plugins/infowidget/geoip/nf.png b/plugins/infowidget/geoip/nf.png
new file mode 100644
index 0000000..a119153
--- /dev/null
+++ b/plugins/infowidget/geoip/nf.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ng.png b/plugins/infowidget/geoip/ng.png
new file mode 100644
index 0000000..44492e1
--- /dev/null
+++ b/plugins/infowidget/geoip/ng.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ni.png b/plugins/infowidget/geoip/ni.png
new file mode 100644
index 0000000..805b800
--- /dev/null
+++ b/plugins/infowidget/geoip/ni.png
Binary files differ
diff --git a/plugins/infowidget/geoip/nl.png b/plugins/infowidget/geoip/nl.png
new file mode 100644
index 0000000..24b3e42
--- /dev/null
+++ b/plugins/infowidget/geoip/nl.png
Binary files differ
diff --git a/plugins/infowidget/geoip/no.png b/plugins/infowidget/geoip/no.png
new file mode 100644
index 0000000..c5d3441
--- /dev/null
+++ b/plugins/infowidget/geoip/no.png
Binary files differ
diff --git a/plugins/infowidget/geoip/np.png b/plugins/infowidget/geoip/np.png
new file mode 100644
index 0000000..35856b5
--- /dev/null
+++ b/plugins/infowidget/geoip/np.png
Binary files differ
diff --git a/plugins/infowidget/geoip/nr.png b/plugins/infowidget/geoip/nr.png
new file mode 100644
index 0000000..e0a801e
--- /dev/null
+++ b/plugins/infowidget/geoip/nr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/nu.png b/plugins/infowidget/geoip/nu.png
new file mode 100644
index 0000000..561ca9e
--- /dev/null
+++ b/plugins/infowidget/geoip/nu.png
Binary files differ
diff --git a/plugins/infowidget/geoip/nz.png b/plugins/infowidget/geoip/nz.png
new file mode 100644
index 0000000..2e2cb1b
--- /dev/null
+++ b/plugins/infowidget/geoip/nz.png
Binary files differ
diff --git a/plugins/infowidget/geoip/om.png b/plugins/infowidget/geoip/om.png
new file mode 100644
index 0000000..f6a8b4f
--- /dev/null
+++ b/plugins/infowidget/geoip/om.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pa.png b/plugins/infowidget/geoip/pa.png
new file mode 100644
index 0000000..97c3335
--- /dev/null
+++ b/plugins/infowidget/geoip/pa.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pe.png b/plugins/infowidget/geoip/pe.png
new file mode 100644
index 0000000..3b8bdf8
--- /dev/null
+++ b/plugins/infowidget/geoip/pe.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pf.png b/plugins/infowidget/geoip/pf.png
new file mode 100644
index 0000000..8b25d20
--- /dev/null
+++ b/plugins/infowidget/geoip/pf.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pg.png b/plugins/infowidget/geoip/pg.png
new file mode 100644
index 0000000..1e2ba72
--- /dev/null
+++ b/plugins/infowidget/geoip/pg.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ph.png b/plugins/infowidget/geoip/ph.png
new file mode 100644
index 0000000..6758586
--- /dev/null
+++ b/plugins/infowidget/geoip/ph.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pk.png b/plugins/infowidget/geoip/pk.png
new file mode 100644
index 0000000..fb7d7a4
--- /dev/null
+++ b/plugins/infowidget/geoip/pk.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pl.png b/plugins/infowidget/geoip/pl.png
new file mode 100644
index 0000000..d0d71b7
--- /dev/null
+++ b/plugins/infowidget/geoip/pl.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pm.png b/plugins/infowidget/geoip/pm.png
new file mode 100644
index 0000000..bb7b62e
--- /dev/null
+++ b/plugins/infowidget/geoip/pm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pn.png b/plugins/infowidget/geoip/pn.png
new file mode 100644
index 0000000..727b47e
--- /dev/null
+++ b/plugins/infowidget/geoip/pn.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pr.png b/plugins/infowidget/geoip/pr.png
new file mode 100644
index 0000000..4487e33
--- /dev/null
+++ b/plugins/infowidget/geoip/pr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ps.png b/plugins/infowidget/geoip/ps.png
new file mode 100644
index 0000000..0a26426
--- /dev/null
+++ b/plugins/infowidget/geoip/ps.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pt.png b/plugins/infowidget/geoip/pt.png
new file mode 100644
index 0000000..8dbc9c9
--- /dev/null
+++ b/plugins/infowidget/geoip/pt.png
Binary files differ
diff --git a/plugins/infowidget/geoip/pw.png b/plugins/infowidget/geoip/pw.png
new file mode 100644
index 0000000..6a79ad8
--- /dev/null
+++ b/plugins/infowidget/geoip/pw.png
Binary files differ
diff --git a/plugins/infowidget/geoip/py.png b/plugins/infowidget/geoip/py.png
new file mode 100644
index 0000000..79fb185
--- /dev/null
+++ b/plugins/infowidget/geoip/py.png
Binary files differ
diff --git a/plugins/infowidget/geoip/qa.png b/plugins/infowidget/geoip/qa.png
new file mode 100644
index 0000000..c74d326
--- /dev/null
+++ b/plugins/infowidget/geoip/qa.png
Binary files differ
diff --git a/plugins/infowidget/geoip/re.png b/plugins/infowidget/geoip/re.png
new file mode 100644
index 0000000..07ba0c7
--- /dev/null
+++ b/plugins/infowidget/geoip/re.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ro.png b/plugins/infowidget/geoip/ro.png
new file mode 100644
index 0000000..09eee0e
--- /dev/null
+++ b/plugins/infowidget/geoip/ro.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ru.png b/plugins/infowidget/geoip/ru.png
new file mode 100644
index 0000000..7944b95
--- /dev/null
+++ b/plugins/infowidget/geoip/ru.png
Binary files differ
diff --git a/plugins/infowidget/geoip/rw.png b/plugins/infowidget/geoip/rw.png
new file mode 100644
index 0000000..1526936
--- /dev/null
+++ b/plugins/infowidget/geoip/rw.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sa.png b/plugins/infowidget/geoip/sa.png
new file mode 100644
index 0000000..5d9420d
--- /dev/null
+++ b/plugins/infowidget/geoip/sa.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sb.png b/plugins/infowidget/geoip/sb.png
new file mode 100644
index 0000000..50a584e
--- /dev/null
+++ b/plugins/infowidget/geoip/sb.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sc.png b/plugins/infowidget/geoip/sc.png
new file mode 100644
index 0000000..0f1ea8c
--- /dev/null
+++ b/plugins/infowidget/geoip/sc.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sd.png b/plugins/infowidget/geoip/sd.png
new file mode 100644
index 0000000..cfd32d9
--- /dev/null
+++ b/plugins/infowidget/geoip/sd.png
Binary files differ
diff --git a/plugins/infowidget/geoip/se.png b/plugins/infowidget/geoip/se.png
new file mode 100644
index 0000000..ba49b92
--- /dev/null
+++ b/plugins/infowidget/geoip/se.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sg.png b/plugins/infowidget/geoip/sg.png
new file mode 100644
index 0000000..db17483
--- /dev/null
+++ b/plugins/infowidget/geoip/sg.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sh.png b/plugins/infowidget/geoip/sh.png
new file mode 100644
index 0000000..fa95fc4
--- /dev/null
+++ b/plugins/infowidget/geoip/sh.png
Binary files differ
diff --git a/plugins/infowidget/geoip/si.png b/plugins/infowidget/geoip/si.png
new file mode 100644
index 0000000..420c623
--- /dev/null
+++ b/plugins/infowidget/geoip/si.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sj.png b/plugins/infowidget/geoip/sj.png
new file mode 100644
index 0000000..316e9e8
--- /dev/null
+++ b/plugins/infowidget/geoip/sj.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sk.png b/plugins/infowidget/geoip/sk.png
new file mode 100644
index 0000000..1abf082
--- /dev/null
+++ b/plugins/infowidget/geoip/sk.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sl.png b/plugins/infowidget/geoip/sl.png
new file mode 100644
index 0000000..824f979
--- /dev/null
+++ b/plugins/infowidget/geoip/sl.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sm.png b/plugins/infowidget/geoip/sm.png
new file mode 100644
index 0000000..e9d4f2b
--- /dev/null
+++ b/plugins/infowidget/geoip/sm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sn.png b/plugins/infowidget/geoip/sn.png
new file mode 100644
index 0000000..c9b026a
--- /dev/null
+++ b/plugins/infowidget/geoip/sn.png
Binary files differ
diff --git a/plugins/infowidget/geoip/so.png b/plugins/infowidget/geoip/so.png
new file mode 100644
index 0000000..c7c155d
--- /dev/null
+++ b/plugins/infowidget/geoip/so.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sr.png b/plugins/infowidget/geoip/sr.png
new file mode 100644
index 0000000..b838461
--- /dev/null
+++ b/plugins/infowidget/geoip/sr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/st.png b/plugins/infowidget/geoip/st.png
new file mode 100644
index 0000000..1be3857
--- /dev/null
+++ b/plugins/infowidget/geoip/st.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sv.png b/plugins/infowidget/geoip/sv.png
new file mode 100644
index 0000000..fc5c5b5
--- /dev/null
+++ b/plugins/infowidget/geoip/sv.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sy.png b/plugins/infowidget/geoip/sy.png
new file mode 100644
index 0000000..87a102d
--- /dev/null
+++ b/plugins/infowidget/geoip/sy.png
Binary files differ
diff --git a/plugins/infowidget/geoip/sz.png b/plugins/infowidget/geoip/sz.png
new file mode 100644
index 0000000..847704f
--- /dev/null
+++ b/plugins/infowidget/geoip/sz.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tc.png b/plugins/infowidget/geoip/tc.png
new file mode 100644
index 0000000..a2dd1e3
--- /dev/null
+++ b/plugins/infowidget/geoip/tc.png
Binary files differ
diff --git a/plugins/infowidget/geoip/td.png b/plugins/infowidget/geoip/td.png
new file mode 100644
index 0000000..8d76050
--- /dev/null
+++ b/plugins/infowidget/geoip/td.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tf.png b/plugins/infowidget/geoip/tf.png
new file mode 100644
index 0000000..8b14629
--- /dev/null
+++ b/plugins/infowidget/geoip/tf.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tg.png b/plugins/infowidget/geoip/tg.png
new file mode 100644
index 0000000..1ab1584
--- /dev/null
+++ b/plugins/infowidget/geoip/tg.png
Binary files differ
diff --git a/plugins/infowidget/geoip/th.png b/plugins/infowidget/geoip/th.png
new file mode 100644
index 0000000..b4af833
--- /dev/null
+++ b/plugins/infowidget/geoip/th.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tj.png b/plugins/infowidget/geoip/tj.png
new file mode 100644
index 0000000..bc08004
--- /dev/null
+++ b/plugins/infowidget/geoip/tj.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tk.png b/plugins/infowidget/geoip/tk.png
new file mode 100644
index 0000000..d18f222
--- /dev/null
+++ b/plugins/infowidget/geoip/tk.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tl.png b/plugins/infowidget/geoip/tl.png
new file mode 100644
index 0000000..3a5a3df
--- /dev/null
+++ b/plugins/infowidget/geoip/tl.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tm.png b/plugins/infowidget/geoip/tm.png
new file mode 100644
index 0000000..bdde4d2
--- /dev/null
+++ b/plugins/infowidget/geoip/tm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tn.png b/plugins/infowidget/geoip/tn.png
new file mode 100644
index 0000000..b1d8786
--- /dev/null
+++ b/plugins/infowidget/geoip/tn.png
Binary files differ
diff --git a/plugins/infowidget/geoip/to.png b/plugins/infowidget/geoip/to.png
new file mode 100644
index 0000000..9ffad3f
--- /dev/null
+++ b/plugins/infowidget/geoip/to.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tp.png b/plugins/infowidget/geoip/tp.png
new file mode 100644
index 0000000..2f9e1c5
--- /dev/null
+++ b/plugins/infowidget/geoip/tp.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tr.png b/plugins/infowidget/geoip/tr.png
new file mode 100644
index 0000000..834ff3b
--- /dev/null
+++ b/plugins/infowidget/geoip/tr.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tt.png b/plugins/infowidget/geoip/tt.png
new file mode 100644
index 0000000..8a4697e
--- /dev/null
+++ b/plugins/infowidget/geoip/tt.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tv.png b/plugins/infowidget/geoip/tv.png
new file mode 100644
index 0000000..3cf5a6a
--- /dev/null
+++ b/plugins/infowidget/geoip/tv.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tw.png b/plugins/infowidget/geoip/tw.png
new file mode 100644
index 0000000..612811e
--- /dev/null
+++ b/plugins/infowidget/geoip/tw.png
Binary files differ
diff --git a/plugins/infowidget/geoip/tz.png b/plugins/infowidget/geoip/tz.png
new file mode 100644
index 0000000..38cf2ae
--- /dev/null
+++ b/plugins/infowidget/geoip/tz.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ua.png b/plugins/infowidget/geoip/ua.png
new file mode 100644
index 0000000..b1f05dd
--- /dev/null
+++ b/plugins/infowidget/geoip/ua.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ug.png b/plugins/infowidget/geoip/ug.png
new file mode 100644
index 0000000..9e18382
--- /dev/null
+++ b/plugins/infowidget/geoip/ug.png
Binary files differ
diff --git a/plugins/infowidget/geoip/um.png b/plugins/infowidget/geoip/um.png
new file mode 100644
index 0000000..02abc0f
--- /dev/null
+++ b/plugins/infowidget/geoip/um.png
Binary files differ
diff --git a/plugins/infowidget/geoip/us.png b/plugins/infowidget/geoip/us.png
new file mode 100644
index 0000000..21aedb8
--- /dev/null
+++ b/plugins/infowidget/geoip/us.png
Binary files differ
diff --git a/plugins/infowidget/geoip/uy.png b/plugins/infowidget/geoip/uy.png
new file mode 100644
index 0000000..94b3e69
--- /dev/null
+++ b/plugins/infowidget/geoip/uy.png
Binary files differ
diff --git a/plugins/infowidget/geoip/uz.png b/plugins/infowidget/geoip/uz.png
new file mode 100644
index 0000000..f336c66
--- /dev/null
+++ b/plugins/infowidget/geoip/uz.png
Binary files differ
diff --git a/plugins/infowidget/geoip/va.png b/plugins/infowidget/geoip/va.png
new file mode 100644
index 0000000..bd6488a
--- /dev/null
+++ b/plugins/infowidget/geoip/va.png
Binary files differ
diff --git a/plugins/infowidget/geoip/vc.png b/plugins/infowidget/geoip/vc.png
new file mode 100644
index 0000000..6b697e6
--- /dev/null
+++ b/plugins/infowidget/geoip/vc.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ve.png b/plugins/infowidget/geoip/ve.png
new file mode 100644
index 0000000..3fbf630
--- /dev/null
+++ b/plugins/infowidget/geoip/ve.png
Binary files differ
diff --git a/plugins/infowidget/geoip/vg.png b/plugins/infowidget/geoip/vg.png
new file mode 100644
index 0000000..6ebc938
--- /dev/null
+++ b/plugins/infowidget/geoip/vg.png
Binary files differ
diff --git a/plugins/infowidget/geoip/vi.png b/plugins/infowidget/geoip/vi.png
new file mode 100644
index 0000000..d98b021
--- /dev/null
+++ b/plugins/infowidget/geoip/vi.png
Binary files differ
diff --git a/plugins/infowidget/geoip/vn.png b/plugins/infowidget/geoip/vn.png
new file mode 100644
index 0000000..23add83
--- /dev/null
+++ b/plugins/infowidget/geoip/vn.png
Binary files differ
diff --git a/plugins/infowidget/geoip/vu.png b/plugins/infowidget/geoip/vu.png
new file mode 100644
index 0000000..131d19a
--- /dev/null
+++ b/plugins/infowidget/geoip/vu.png
Binary files differ
diff --git a/plugins/infowidget/geoip/wf.png b/plugins/infowidget/geoip/wf.png
new file mode 100644
index 0000000..9576dc7
--- /dev/null
+++ b/plugins/infowidget/geoip/wf.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ws.png b/plugins/infowidget/geoip/ws.png
new file mode 100644
index 0000000..dc44158
--- /dev/null
+++ b/plugins/infowidget/geoip/ws.png
Binary files differ
diff --git a/plugins/infowidget/geoip/ye.png b/plugins/infowidget/geoip/ye.png
new file mode 100644
index 0000000..245f46f
--- /dev/null
+++ b/plugins/infowidget/geoip/ye.png
Binary files differ
diff --git a/plugins/infowidget/geoip/yt.png b/plugins/infowidget/geoip/yt.png
new file mode 100644
index 0000000..3e6ef07
--- /dev/null
+++ b/plugins/infowidget/geoip/yt.png
Binary files differ
diff --git a/plugins/infowidget/geoip/yu.png b/plugins/infowidget/geoip/yu.png
new file mode 100644
index 0000000..b00030c
--- /dev/null
+++ b/plugins/infowidget/geoip/yu.png
Binary files differ
diff --git a/plugins/infowidget/geoip/za.png b/plugins/infowidget/geoip/za.png
new file mode 100644
index 0000000..c601244
--- /dev/null
+++ b/plugins/infowidget/geoip/za.png
Binary files differ
diff --git a/plugins/infowidget/geoip/zm.png b/plugins/infowidget/geoip/zm.png
new file mode 100644
index 0000000..201f4cf
--- /dev/null
+++ b/plugins/infowidget/geoip/zm.png
Binary files differ
diff --git a/plugins/infowidget/geoip/zw.png b/plugins/infowidget/geoip/zw.png
new file mode 100644
index 0000000..66754d4
--- /dev/null
+++ b/plugins/infowidget/geoip/zw.png
Binary files differ
diff --git a/plugins/infowidget/infowidgetplugin.cpp b/plugins/infowidget/infowidgetplugin.cpp
new file mode 100644
index 0000000..4957c48
--- /dev/null
+++ b/plugins/infowidget/infowidgetplugin.cpp
@@ -0,0 +1,244 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qscrollview.h>
+#include <kgenericfactory.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <interfaces/coreinterface.h>
+#include <interfaces/guiinterface.h>
+#include <interfaces/torrentinterface.h>
+
+#include "infowidgetplugin.h"
+#include "infowidgetprefpage.h"
+#include "statustab.h"
+#include "fileview.h"
+#include "chunkdownloadview.h"
+#include "peerview.h"
+#include "trackerview.h"
+#include "infowidgetpluginsettings.h"
+#include "ktorrentmonitor.h"
+
+
+#define NAME "Info Widget"
+#define AUTHOR "Joris Guisson"
+#define EMAIL "joris.guisson@gmail.com"
+
+
+K_EXPORT_COMPONENT_FACTORY(ktinfowidgetplugin,KGenericFactory<kt::InfoWidgetPlugin>("ktinfowidgetplugin"))
+
+namespace kt
+{
+
+
+ InfoWidgetPlugin::InfoWidgetPlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name, args,NAME,i18n("Info Widget"),AUTHOR,EMAIL,
+ i18n("Shows additional information about a download. Like which chunks have been downloaded, how many seeders and leechers ..."),
+ "ktinfowidget")
+ {
+ pref = 0;
+ peer_view = 0;
+ cd_view = 0;
+ tracker_view = 0;
+ file_view = 0;
+ status_tab = 0;
+ monitor = 0;
+ }
+
+
+ InfoWidgetPlugin::~InfoWidgetPlugin()
+ {}
+
+
+ void InfoWidgetPlugin::load()
+ {
+ status_tab = new StatusTab(0);
+ file_view = new FileView(0);
+
+ pref = new InfoWidgetPrefPage(this);
+ getGUI()->addViewListener(this);
+ getGUI()->addToolWidget(status_tab,"info",i18n("Status"),GUIInterface::DOCK_BOTTOM);
+ getGUI()->addToolWidget(file_view,"folder",i18n("Files"),GUIInterface::DOCK_BOTTOM);
+
+ showPeerView( InfoWidgetPluginSettings::showPeerView() );
+ showChunkView( InfoWidgetPluginSettings::showChunkView() );
+ showTrackerView( InfoWidgetPluginSettings::showTrackersView() );
+
+ getGUI()->addPrefPage(pref);
+ currentTorrentChanged(const_cast<kt::TorrentInterface*>(getGUI()->getCurrentTorrent()));
+
+ file_view->restoreLayout(KGlobal::config(),"FileView");
+ }
+
+ void InfoWidgetPlugin::unload()
+ {
+ if (cd_view)
+ cd_view->saveLayout(KGlobal::config(),"ChunkDownloadView");
+ if (peer_view)
+ peer_view->saveLayout(KGlobal::config(),"PeerView");
+ if (file_view)
+ file_view->saveLayout(KGlobal::config(),"FileView");
+
+ getGUI()->removeViewListener(this);
+ getGUI()->removePrefPage(pref);
+ getGUI()->removeToolWidget(status_tab);
+ getGUI()->removeToolWidget(file_view);
+ if (cd_view)
+ getGUI()->removeToolWidget(cd_view);
+ if (tracker_view)
+ getGUI()->removeToolWidget(tracker_view);
+ if (peer_view)
+ getGUI()->removeToolWidget(peer_view);
+
+ delete monitor;
+ monitor = 0;
+ delete status_tab;
+ status_tab = 0;
+ delete file_view;
+ file_view = 0;
+ delete cd_view;
+ cd_view = 0;
+ delete peer_view;
+ peer_view = 0;
+ delete tracker_view;
+ tracker_view = 0;
+ delete pref;
+ pref = 0;
+ }
+
+ void InfoWidgetPlugin::guiUpdate()
+ {
+ if (status_tab && status_tab->isVisible())
+ status_tab->update();
+
+ if (file_view && file_view->isVisible())
+ file_view->update();
+
+ if (peer_view && peer_view->isVisible())
+ peer_view->update();
+
+ if (cd_view && cd_view->isVisible())
+ cd_view->update();
+
+ if (tracker_view && tracker_view->isVisible())
+ tracker_view->update();
+ }
+
+ void InfoWidgetPlugin::currentTorrentChanged(TorrentInterface* tc)
+ {
+ if (status_tab)
+ status_tab->changeTC(tc);
+ if (file_view)
+ file_view->changeTC(tc);
+ if (cd_view)
+ cd_view->changeTC(tc);
+ if (tracker_view)
+ tracker_view->changeTC(tc);
+
+ if (peer_view)
+ peer_view->setEnabled(tc != 0);
+
+ createMonitor(tc);
+ }
+
+ bool InfoWidgetPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+
+
+ void InfoWidgetPlugin::showPeerView(bool show)
+ {
+ kt::TorrentInterface* tc = const_cast<kt::TorrentInterface*>(getGUI()->getCurrentTorrent());
+
+ if (show && !peer_view)
+ {
+ peer_view = new PeerView(0);
+ getGUI()->addToolWidget(peer_view,"kdmconfig",i18n("Peers"),GUIInterface::DOCK_BOTTOM);
+
+ peer_view->restoreLayout(KGlobal::config(),"PeerView");
+ createMonitor(tc);
+ }
+ else if (!show && peer_view)
+ {
+ peer_view->saveLayout(KGlobal::config(),"PeerView");
+ getGUI()->removeToolWidget(peer_view);
+ delete peer_view; peer_view = 0;
+ createMonitor(tc);
+ }
+ }
+
+ void InfoWidgetPlugin::showChunkView(bool show)
+ {
+ kt::TorrentInterface* tc = const_cast<kt::TorrentInterface*>(getGUI()->getCurrentTorrent());
+
+ if (show && !cd_view)
+ {
+ cd_view = new ChunkDownloadView(0);
+ getGUI()->addToolWidget(cd_view,"fifteenpieces",i18n("Chunks"),GUIInterface::DOCK_BOTTOM);
+
+ cd_view->restoreLayout(KGlobal::config(),"ChunkDownloadView");
+ cd_view->changeTC(tc);
+ createMonitor(tc);
+ }
+ else if (!show && cd_view)
+ {
+ cd_view->saveLayout(KGlobal::config(),"ChunkDownloadView");
+ getGUI()->removeToolWidget(cd_view);
+ delete cd_view; cd_view = 0;
+ createMonitor(tc);
+ }
+ }
+
+ void InfoWidgetPlugin::showTrackerView(bool show)
+ {
+ if (show && !tracker_view)
+ {
+ tracker_view = new TrackerView(0);
+ getGUI()->addToolWidget(tracker_view,"network",i18n("Trackers"),
+ GUIInterface::DOCK_BOTTOM);
+ tracker_view->changeTC(const_cast<kt::TorrentInterface*>(getGUI()->getCurrentTorrent()));
+ // seeing that a merge of the trackers might happen after a torrent has been loaded
+ // we need to update the tracker_view
+ connect(getCore(),SIGNAL(loadingFinished(const KURL&, bool, bool)),
+ tracker_view,SLOT(onLoadingFinished(const KURL&, bool, bool)));
+ }
+ else if (!show && tracker_view)
+ {
+ getGUI()->removeToolWidget(tracker_view);
+ delete tracker_view; tracker_view = 0;
+ }
+ }
+
+ void InfoWidgetPlugin::createMonitor(TorrentInterface* tc)
+ {
+ if (monitor)
+ delete monitor; monitor = 0;
+
+ if (peer_view)
+ peer_view->removeAll();
+ if (cd_view)
+ cd_view->removeAll();
+
+ if (tc && (peer_view || cd_view))
+ monitor = new KTorrentMonitor(tc,peer_view,cd_view);
+ }
+}
+
+#include "infowidgetplugin.moc"
diff --git a/plugins/infowidget/infowidgetplugin.h b/plugins/infowidget/infowidgetplugin.h
new file mode 100644
index 0000000..9e08e75
--- /dev/null
+++ b/plugins/infowidget/infowidgetplugin.h
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTINFOWIDGETPLUGIN_H
+#define KTINFOWIDGETPLUGIN_H
+
+#include <interfaces/plugin.h>
+#include <interfaces/guiinterface.h>
+
+
+
+namespace kt
+{
+ class PeerView;
+ class TrackerView;
+ class StatusTab;
+ class FileView;
+ class ChunkDownloadView;
+ class InfoWidgetPrefPage;
+ class KTorrentMonitor;
+
+
+ /**
+ @author Joris Guisson
+ */
+ class InfoWidgetPlugin : public Plugin,public ViewListener
+ {
+ Q_OBJECT
+ public:
+ InfoWidgetPlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~InfoWidgetPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual void guiUpdate();
+ virtual void currentTorrentChanged(TorrentInterface* tc);
+ virtual bool versionCheck(const QString & version) const;
+
+ ///Show PeerView in main window
+ void showPeerView(bool show);
+ ///Show ChunkDownloadView in main window
+ void showChunkView(bool show);
+ ///Show TrackerView in main window
+ void showTrackerView(bool show);
+ private:
+ void createMonitor(TorrentInterface* tc);
+
+ private:
+ PeerView* peer_view;
+ ChunkDownloadView* cd_view;
+ TrackerView* tracker_view;
+ FileView* file_view;
+ StatusTab* status_tab;
+ KTorrentMonitor* monitor;
+
+ InfoWidgetPrefPage* pref;
+ };
+
+}
+
+#endif
diff --git a/plugins/infowidget/infowidgetpluginsettings.kcfgc b/plugins/infowidget/infowidgetpluginsettings.kcfgc
new file mode 100644
index 0000000..39b3109
--- /dev/null
+++ b/plugins/infowidget/infowidgetpluginsettings.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=ktinfowidgetplugin.kcfg
+ClassName=InfoWidgetPluginSettings
+Namespace=kt
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables
diff --git a/plugins/infowidget/infowidgetprefpage.cpp b/plugins/infowidget/infowidgetprefpage.cpp
new file mode 100644
index 0000000..16e1384
--- /dev/null
+++ b/plugins/infowidget/infowidgetprefpage.cpp
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <qcheckbox.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include "infowidgetplugin.h"
+#include "infowidgetprefpage.h"
+#include "infowidgetpluginsettings.h"
+#include "iwpref.h"
+
+
+namespace kt
+{
+
+ InfoWidgetPrefPage::InfoWidgetPrefPage(InfoWidgetPlugin* iw)
+ : PrefPageInterface(i18n("Info Widget"),i18n("Information Widget Options"),KGlobal::iconLoader()->loadIcon("ktinfowidget",KIcon::NoGroup)),iw(iw)
+ {
+ pref = 0;
+ }
+
+
+ InfoWidgetPrefPage::~InfoWidgetPrefPage()
+ {}
+
+
+ bool InfoWidgetPrefPage::apply()
+ {
+ InfoWidgetPluginSettings::setShowPeerView(pref->m_show_pv->isChecked());
+ InfoWidgetPluginSettings::setShowChunkView(pref->m_show_cdv->isChecked());
+ InfoWidgetPluginSettings::setShowTrackersView(pref->m_show_tv->isChecked());
+ InfoWidgetPluginSettings::writeConfig();
+ iw->showPeerView( InfoWidgetPluginSettings::showPeerView() );
+ iw->showChunkView( InfoWidgetPluginSettings::showChunkView() );
+ iw->showTrackerView( InfoWidgetPluginSettings::showTrackersView() );
+ return true;
+ }
+
+ void InfoWidgetPrefPage::createWidget(QWidget* parent)
+ {
+ pref = new IWPref(parent);
+ updateData();
+ }
+
+ void InfoWidgetPrefPage::deleteWidget()
+ {
+ delete pref;
+ }
+
+ void InfoWidgetPrefPage::updateData()
+ {
+ pref->m_show_pv->setChecked(InfoWidgetPluginSettings::showPeerView());
+ pref->m_show_cdv->setChecked(InfoWidgetPluginSettings::showChunkView());
+ pref->m_show_tv->setChecked(InfoWidgetPluginSettings::showTrackersView());
+ }
+
+}
diff --git a/plugins/infowidget/infowidgetprefpage.h b/plugins/infowidget/infowidgetprefpage.h
new file mode 100644
index 0000000..a1fa40f
--- /dev/null
+++ b/plugins/infowidget/infowidgetprefpage.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTINFOWIDGETPREFPAGE_H
+#define KTINFOWIDGETPREFPAGE_H
+
+#include <interfaces/prefpageinterface.h>
+
+class IWPref;
+
+namespace kt
+{
+ class InfoWidgetPlugin;
+
+
+ /**
+ @author Joris Guisson
+ */
+ class InfoWidgetPrefPage : public PrefPageInterface
+ {
+ InfoWidgetPlugin* iw;
+ IWPref* pref;
+ public:
+ InfoWidgetPrefPage(InfoWidgetPlugin* iw);
+ virtual ~InfoWidgetPrefPage();
+
+ virtual bool apply();
+ virtual void createWidget(QWidget* parent);
+ virtual void deleteWidget();
+ virtual void updateData();
+
+ };
+
+}
+
+#endif
diff --git a/plugins/infowidget/iwfiletreediritem.cpp b/plugins/infowidget/iwfiletreediritem.cpp
new file mode 100644
index 0000000..1ddf899
--- /dev/null
+++ b/plugins/infowidget/iwfiletreediritem.cpp
@@ -0,0 +1,224 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <util/functions.h>
+#include <interfaces/torrentfileinterface.h>
+#include <interfaces/torrentinterface.h>
+#include "iwfiletreediritem.h"
+#include "iwfiletreeitem.h"
+#include "functions.h"
+
+using namespace kt;
+
+namespace kt
+{
+
+ IWFileTreeDirItem::IWFileTreeDirItem(KListView* klv,const QString & name)
+ : kt::FileTreeDirItem(klv,name)
+ {
+ }
+
+ IWFileTreeDirItem::IWFileTreeDirItem(IWFileTreeDirItem* parent,const QString & name)
+ : kt::FileTreeDirItem(parent,name)
+ {
+ }
+
+ IWFileTreeDirItem::~IWFileTreeDirItem()
+ {
+ }
+
+
+ void IWFileTreeDirItem::updatePercentageInformation()
+ {
+ // first set all the child items
+ bt::PtrMap<QString,FileTreeItem>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ IWFileTreeItem* item = (IWFileTreeItem*)i->second;
+ item->updatePercentageInformation();
+ i++;
+ }
+
+ // then recursivly move on to subdirs
+ bt::PtrMap<QString,FileTreeDirItem>::iterator j = subdirs.begin();
+ while (j != subdirs.end())
+ {
+ ((IWFileTreeDirItem*)j->second)->updatePercentageInformation();
+ j++;
+ }
+ }
+
+ void IWFileTreeDirItem::updatePreviewInformation(kt::TorrentInterface* tc)
+ {
+ // first set all the child items
+ bt::PtrMap<QString,FileTreeItem>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ IWFileTreeItem* item = (IWFileTreeItem*)i->second;
+ item->updatePreviewInformation(tc);
+ i++;
+ }
+
+ // then recursivly move on to subdirs
+ bt::PtrMap<QString,FileTreeDirItem>::iterator j = subdirs.begin();
+ while (j != subdirs.end())
+ {
+ ((IWFileTreeDirItem*)j->second)->updatePreviewInformation(tc);
+ j++;
+ }
+ }
+
+ Priority IWFileTreeDirItem::updatePriorityInformation(kt::TorrentInterface* tc)
+ {
+ // first set all the child items
+ bt::PtrMap<QString,FileTreeItem>::iterator i = children.begin();
+ bool setpriority = false;
+ bool oneexcluded = false;
+ Priority priority = PREVIEW_PRIORITY;
+ if(i != children.end())
+ {
+ IWFileTreeItem* item = (IWFileTreeItem*)i->second;
+ item->updatePriorityInformation(tc);
+ i++;
+ priority = item->getTorrentFile().getPriority();
+ if(priority == EXCLUDED)
+ oneexcluded = true;
+ setpriority = true;
+ }
+ while (i != children.end())
+ {
+ IWFileTreeItem* item = (IWFileTreeItem*)i->second;
+ item->updatePriorityInformation(tc);
+ i++;
+ if(item->getTorrentFile().getPriority() != priority)
+ setpriority = false;
+ if(item->getTorrentFile().getPriority() == EXCLUDED)
+ oneexcluded = true;
+ }
+
+ // then recursivly move on to subdirs
+ bt::PtrMap<QString,FileTreeDirItem>::iterator j = subdirs.begin();
+ if(j != subdirs.end() && children.begin() == children.end())
+ {
+ Priority priority =
+ ((IWFileTreeDirItem*)j->second)->updatePriorityInformation(tc);
+ if(priority != PREVIEW_PRIORITY)
+ setpriority = true;
+ if(priority == EXCLUDED)
+ oneexcluded = true;
+ j++;
+ }
+
+ while (j != subdirs.end())
+ {
+ if(((IWFileTreeDirItem*)j->second)->updatePriorityInformation(tc)
+ != priority)
+ setpriority = false;
+ if(((IWFileTreeDirItem*)j->second)->updatePriorityInformation(tc)
+ == EXCLUDED)
+ oneexcluded = true;
+ j++;
+ }
+
+ if(setpriority)
+ {
+ switch(priority)
+ {
+ case FIRST_PRIORITY:
+ setText(2, i18n("Yes, First"));
+ childStateChange();
+ break;
+ case LAST_PRIORITY:
+ setText(2, i18n("Yes, Last"));
+ childStateChange();
+ break;
+ case EXCLUDED:
+ setText(2, i18n("No"));
+ childStateChange();
+ break;
+ default:
+ setText(2, i18n("Yes"));
+ childStateChange();
+ break;
+ }
+ return priority;
+ }
+ if(oneexcluded)
+ {
+ setText(2, i18n("No"));
+ childStateChange();
+ }
+ else
+ {
+ setText(2, i18n("Yes"));
+ childStateChange();
+ }
+ return PREVIEW_PRIORITY;
+ }
+
+ FileTreeItem* IWFileTreeDirItem::newFileTreeItem(const QString & name,TorrentFileInterface & file)
+ {
+ return new IWFileTreeItem(this,name,file);
+ }
+
+ FileTreeDirItem* IWFileTreeDirItem::newFileTreeDirItem(const QString & subdir)
+ {
+ return new IWFileTreeDirItem(this,subdir);
+ }
+
+ void IWFileTreeDirItem::updateDNDInformation()
+ {
+ // first set all the child items
+ bt::PtrMap<QString,FileTreeItem>::iterator i = children.begin();
+ while (i != children.end())
+ {
+ IWFileTreeItem* item = (IWFileTreeItem*)i->second;
+ item->updateDNDInformation();
+ i++;
+ }
+
+ // then recursivly move on to subdirs
+ bt::PtrMap<QString,FileTreeDirItem>::iterator j = subdirs.begin();
+ while (j != subdirs.end())
+ {
+ ((IWFileTreeDirItem*)j->second)->updateDNDInformation();
+ j++;
+ }
+ }
+
+ bt::ConfirmationResult IWFileTreeDirItem::confirmationDialog()
+ {
+ return bt::KEEP_DATA;
+/* QString msg = i18n("Do you want to keep the existing data for seeding ?");
+ int ret = KMessageBox::warningYesNoCancel(0,msg,QString::null,
+ KGuiItem(i18n("Keep the data")),
+ KGuiItem(i18n("Delete the data")));
+ if (ret == KMessageBox::Yes)
+ return bt::KEEP_DATA;
+ else if (ret == KMessageBox::No)
+ return bt::THROW_AWAY_DATA;
+ else
+ return bt::CANCELED;
+ */
+ }
+}
diff --git a/plugins/infowidget/iwfiletreediritem.h b/plugins/infowidget/iwfiletreediritem.h
new file mode 100644
index 0000000..90aa7ea
--- /dev/null
+++ b/plugins/infowidget/iwfiletreediritem.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef IWFILETREEDIRITEM_H
+#define IWFILETREEDIRITEM_H
+
+#include <interfaces/filetreediritem.h>
+
+class IWFileTreeItem;
+
+using bt::Uint32;
+using bt::Priority;
+using bt::FIRST_PRIORITY;
+using bt::NORMAL_PRIORITY;
+using bt::LAST_PRIORITY;
+using bt::PREVIEW_PRIORITY;
+using bt::EXCLUDED;
+
+namespace bt
+{
+ class TorrentFile;
+ class TorrentInterface;
+}
+
+namespace kt
+{
+ using namespace bt;
+ /**
+ * @author Joris Guisson
+ *
+ * Directory item in the InfoWidget's file view.
+ */
+ class IWFileTreeDirItem : public kt::FileTreeDirItem
+ {
+ public:
+ IWFileTreeDirItem(KListView* klv,const QString & name);
+ IWFileTreeDirItem(IWFileTreeDirItem* parent,const QString & name);
+ virtual ~IWFileTreeDirItem();
+
+ /**
+ * Update the preview information.
+ * @param tc The TorrentInterface object
+ */
+ void updatePreviewInformation(kt::TorrentInterface* tc);
+
+ /**
+ * Update the downloaded percentage information.
+ */
+ void updatePercentageInformation();
+
+ Priority updatePriorityInformation(kt::TorrentInterface* tc);
+
+ /**
+ * Update the DND information of each file item.
+ */
+ void updateDNDInformation();
+
+ virtual kt::FileTreeItem* newFileTreeItem(const QString & name, kt::TorrentFileInterface & file);
+ virtual kt::FileTreeDirItem* newFileTreeDirItem(const QString & subdir);
+ virtual bt::ConfirmationResult confirmationDialog();
+ };
+}
+
+#endif
diff --git a/plugins/infowidget/iwfiletreeitem.cpp b/plugins/infowidget/iwfiletreeitem.cpp
new file mode 100644
index 0000000..e6281df
--- /dev/null
+++ b/plugins/infowidget/iwfiletreeitem.cpp
@@ -0,0 +1,166 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <interfaces/functions.h>
+#include <interfaces/torrentinterface.h>
+#include <interfaces/torrentfileinterface.h>
+#include <util/bitset.h>
+#include "iwfiletreeitem.h"
+#include "iwfiletreediritem.h"
+#include "functions.h"
+
+using namespace kt;
+
+namespace kt
+{
+
+ IWFileTreeItem::IWFileTreeItem(IWFileTreeDirItem* item,const QString & name,kt::TorrentFileInterface & file)
+ : FileTreeItem(item,name,file)
+ {
+ perc_complete = 0.0;
+ connect(&file,SIGNAL(downloadPercentageChanged( float )),this,SLOT(onPercentageUpdated( float )));
+ connect(&file,SIGNAL(previewAvailable( bool )),this,SLOT(onPreviewAvailable( bool )));
+ }
+
+ IWFileTreeItem::~IWFileTreeItem()
+ {
+ }
+
+ int IWFileTreeItem::compare(QListViewItem* i, int col, bool ascending) const
+ {
+ if (col == 4)
+ {
+ IWFileTreeItem* other = dynamic_cast<IWFileTreeItem*>(i);
+ if (!other)
+ return 0;
+ else
+ return CompareVal(perc_complete,other->perc_complete);
+ }
+ else
+ {
+ return FileTreeItem::compare(i, col, ascending);
+ }
+ }
+
+
+ void IWFileTreeItem::updatePreviewInformation(kt::TorrentInterface* tc)
+ {
+ if (file.isMultimedia())
+ {
+ if (tc->readyForPreview(file.getFirstChunk(), file.getFirstChunk()+1) )
+ {
+ setText(3, i18n("Available"));
+ }
+ else
+ {
+ setText(3, i18n("Pending"));
+ }
+ }
+ else
+ setText(3, i18n("No"));
+ }
+
+ void IWFileTreeItem::updatePercentageInformation()
+ {
+ onPercentageUpdated(file.getDownloadPercentage());
+ }
+
+ void IWFileTreeItem::onPercentageUpdated(float p)
+ {
+ double percent = p;
+ if (percent < 0.0)
+ percent = 0.0;
+ else if (percent > 100.0)
+ percent = 100.0;
+ KLocale* loc = KGlobal::locale();
+ setText(4,i18n("%1 %").arg(loc->formatNumber(percent,2)));
+ perc_complete = percent;
+ }
+
+ void IWFileTreeItem::onPreviewAvailable(bool av)
+ {
+ if (av)
+ {
+ setText(3, i18n("Available"));
+ }
+ else if (file.isMultimedia())
+ {
+ setText(3, i18n("Pending"));
+ }
+ else
+ {
+ setText(3, i18n("No"));
+ }
+ }
+
+ void IWFileTreeItem::updatePriorityInformation(kt::TorrentInterface* tc)
+ {
+ switch(file.getPriority())
+ {
+ case FIRST_PRIORITY:
+ setText(2, i18n("Yes, First"));
+ break;
+ case LAST_PRIORITY:
+ setText(2, i18n("Yes, Last"));
+ break;
+ case ONLY_SEED_PRIORITY:
+ case EXCLUDED:
+ setText(2, i18n("No"));
+ break;
+ case PREVIEW_PRIORITY:
+ break;
+ default:
+ setText(2, i18n("Yes"));
+ break;
+ }
+ }
+
+ void IWFileTreeItem::updateDNDInformation()
+ {
+ if (file.doNotDownload() && isOn())
+ {
+ setChecked(false);
+ setText(2, i18n("No"));
+ }
+ }
+
+ bt::ConfirmationResult IWFileTreeItem::confirmationDialog()
+ {
+ return bt::KEEP_DATA;
+ /*
+ QString msg = i18n("Do you want to keep the existing data for seeding ?");
+ int ret = KMessageBox::warningYesNoCancel(0,msg,QString::null,
+ KGuiItem(i18n("Keep the data")),
+ KGuiItem(i18n("Delete the data")));
+ if (ret == KMessageBox::Yes)
+ return bt::KEEP_DATA;
+ else if (ret == KMessageBox::No)
+ return bt::THROW_AWAY_DATA;
+ else
+ return bt::CANCELED;
+ */
+ }
+
+}
+
+#include "iwfiletreeitem.moc"
+
diff --git a/plugins/infowidget/iwfiletreeitem.h b/plugins/infowidget/iwfiletreeitem.h
new file mode 100644
index 0000000..7877ff1
--- /dev/null
+++ b/plugins/infowidget/iwfiletreeitem.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef IWFILETREEITEM_H
+#define IWFILETREEITEM_H
+
+#include <qobject.h>
+#include <interfaces/filetreeitem.h>
+
+using namespace bt;
+
+namespace kt
+{
+ class TorrentFileInterface;
+ class TorrentInterface;
+ class IWFileTreeDirItem;
+
+ /**
+ * @author Joris Guisson
+ *
+ * File item in the InfoWidget's file view.
+ */
+ class IWFileTreeItem : public QObject, public kt::FileTreeItem
+ {
+ Q_OBJECT
+
+ double perc_complete;
+ public:
+ IWFileTreeItem(IWFileTreeDirItem* item,const QString & name,kt::TorrentFileInterface & file);
+ virtual ~IWFileTreeItem();
+
+ void updatePreviewInformation(kt::TorrentInterface* tc);
+ void updatePercentageInformation();
+ void updatePriorityInformation(kt::TorrentInterface* tc);
+ void updateDNDInformation();
+ protected:
+ virtual int compare(QListViewItem* i, int col, bool ascending) const;
+ virtual bt::ConfirmationResult confirmationDialog();
+
+ protected slots:
+ void onPercentageUpdated(float p);
+ void onPreviewAvailable(bool av);
+ };
+}
+
+
+
+#endif
diff --git a/plugins/infowidget/iwpref.ui b/plugins/infowidget/iwpref.ui
new file mode 100644
index 0000000..1007259
--- /dev/null
+++ b/plugins/infowidget/iwpref.ui
@@ -0,0 +1,69 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>IWPref</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>IWPref</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>138</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>m_show_pv</cstring>
+ </property>
+ <property name="text">
+ <string>Show list of peers</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>m_show_cdv</cstring>
+ </property>
+ <property name="text">
+ <string>Show list of chunks currentl&amp;y downloading</string>
+ </property>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>m_show_tv</cstring>
+ </property>
+ <property name="text">
+ <string>Show list of trackers</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/plugins/infowidget/ktinfowidgetplugin.desktop b/plugins/infowidget/ktinfowidgetplugin.desktop
new file mode 100644
index 0000000..535f90c
--- /dev/null
+++ b/plugins/infowidget/ktinfowidgetplugin.desktop
@@ -0,0 +1,29 @@
+[Desktop Entry]
+Name=SearchPlugin
+Name[bg]=Приставка за търсене
+Name[br]=Lugent klask
+Name[da]=SøgePlugin
+Name[de]=Suche-Modul
+Name[el]=Πρόσθετο αναζήτησης
+Name[es]=Complemento de búsqueda
+Name[et]=Otsimisplugin
+Name[fa]=وصلۀ جستجو
+Name[it]=Plugin di ricerca
+Name[nb]=Søkemodul
+Name[nds]=Söök-Moduul
+Name[nl]=Zoekplugin
+Name[pl]=Wtyczka wyszukiwania
+Name[pt]='Plugin' de Procura
+Name[pt_BR]=Plugin de Busca
+Name[sk]=Vyhľadávací Plugin
+Name[sr]=Прикључак претраге
+Name[sr@Latn]=Priključak pretrage
+Name[sv]=Sökinsticksprogram
+Name[tr]=Arama Eklentisi
+Name[uk]=Втулок пошуку
+Name[xx]=xxSearchPluginxx
+Name[zh_CN]=搜索插件
+Name[zh_TW]=搜尋外掛程式
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktinfowidgetplugin
diff --git a/plugins/infowidget/ktinfowidgetplugin.kcfg b/plugins/infowidget/ktinfowidgetplugin.kcfg
new file mode 100644
index 0000000..51048fa
--- /dev/null
+++ b/plugins/infowidget/ktinfowidgetplugin.kcfg
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <kcfgfile name="ktinfowidgetpluginrc"/>
+ <group name="general">
+ <entry name="showPeerView" type="Bool">
+ <label>Show peer view tab in main window</label>
+ <default>true</default>
+ </entry>
+ <entry name="showChunkView" type="Bool">
+ <label>Show chunk download view tab in main window</label>
+ <default>true</default>
+ </entry>
+ <entry name="showTrackersView" type="Bool">
+ <label>Show trackers view tab in main window</label>
+ <default>true</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/infowidget/ktorrentmonitor.cpp b/plugins/infowidget/ktorrentmonitor.cpp
new file mode 100644
index 0000000..7494a5c
--- /dev/null
+++ b/plugins/infowidget/ktorrentmonitor.cpp
@@ -0,0 +1,88 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <interfaces/peerinterface.h>
+#include <interfaces/torrentinterface.h>
+#include <interfaces/chunkdownloadinterface.h>
+#include "ktorrentmonitor.h"
+#include "peerview.h"
+#include "chunkdownloadview.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ KTorrentMonitor::KTorrentMonitor(kt::TorrentInterface* tc,
+ PeerView* pv,
+ ChunkDownloadView* cdv) : tc(tc),pv(pv),cdv(cdv)
+ {
+ if (tc)
+ tc->setMonitor(this);
+ }
+
+
+ KTorrentMonitor::~KTorrentMonitor()
+ {
+ if (tc)
+ tc->setMonitor(0);
+ }
+
+
+ void KTorrentMonitor::downloadRemoved(kt::ChunkDownloadInterface* cd)
+ {
+ if (cdv)
+ cdv->removeDownload(cd);
+ }
+
+ void KTorrentMonitor::downloadStarted(kt::ChunkDownloadInterface* cd)
+ {
+ if (cdv)
+ cdv->addDownload(cd);
+ }
+
+ void KTorrentMonitor::peerAdded(kt::PeerInterface* peer)
+ {
+ if (pv)
+ pv->addPeer(peer);
+ }
+
+ void KTorrentMonitor::peerRemoved(kt::PeerInterface* peer)
+ {
+ if (pv)
+ pv->removePeer(peer);
+ }
+
+ void KTorrentMonitor::stopped()
+ {
+ if (pv)
+ pv->removeAll();
+ if (cdv)
+ cdv->removeAll();
+ }
+
+ void KTorrentMonitor::destroyed()
+ {
+ if (pv)
+ pv->removeAll();
+ if (cdv)
+ cdv->removeAll();
+ tc = 0;
+ }
+}
diff --git a/plugins/infowidget/ktorrentmonitor.h b/plugins/infowidget/ktorrentmonitor.h
new file mode 100644
index 0000000..6a312a2
--- /dev/null
+++ b/plugins/infowidget/ktorrentmonitor.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTORRENTMONITOR_H
+#define KTORRENTMONITOR_H
+
+#include <interfaces/monitorinterface.h>
+
+
+
+namespace kt
+{
+ class TorrentInterface;
+ class PeerView;
+ class ChunkDownloadView;
+
+ /**
+ @author Joris Guisson
+ */
+ class KTorrentMonitor : public kt::MonitorInterface
+ {
+ kt::TorrentInterface* tc;
+ PeerView* pv;
+ ChunkDownloadView* cdv;
+ public:
+ KTorrentMonitor(
+ kt::TorrentInterface* tc,
+ PeerView* pv,
+ ChunkDownloadView* cdv);
+ virtual ~KTorrentMonitor();
+
+ virtual void downloadRemoved(kt::ChunkDownloadInterface* cd);
+ virtual void downloadStarted(kt::ChunkDownloadInterface* cd);
+ virtual void peerAdded(kt::PeerInterface* peer);
+ virtual void peerRemoved(kt::PeerInterface* peer);
+ virtual void stopped();
+ virtual void destroyed();
+
+ };
+}
+
+#endif
diff --git a/plugins/infowidget/localefloatvalidator.cpp b/plugins/infowidget/localefloatvalidator.cpp
new file mode 100644
index 0000000..7faafed
--- /dev/null
+++ b/plugins/infowidget/localefloatvalidator.cpp
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * Copyright (C) 2006 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Vincent Wagelaar <vincent@ricardis.tudelft.nl> *
+ * Jonas Widarsson <jonas@widarsson.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kglobal.h>
+#include <klocale.h>
+#include "localefloatvalidator.h"
+
+kt::LocaleFloatValidator::LocaleFloatValidator( QObject * parent, const char * name )
+:QValidator(parent, name)
+{
+ QString decimalPoint = QRegExp::escape(KGlobal::locale()->decimalSymbol());
+ regexp.setPattern("^-?\\d*(" + decimalPoint + "\\d*)?$");
+}
+
+QValidator::State kt::LocaleFloatValidator::validate( QString & str, int & pos) const
+{
+ return regexp.exactMatch(str) ? QValidator::Acceptable : QValidator::Invalid;
+}
+
+
+#include "localefloatvalidator.moc"
diff --git a/plugins/infowidget/localefloatvalidator.h b/plugins/infowidget/localefloatvalidator.h
new file mode 100644
index 0000000..d4d12ee
--- /dev/null
+++ b/plugins/infowidget/localefloatvalidator.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright (C) 2005 by *
+ * Joris Guisson <joris.guisson@gmail.com> *
+ * Vincent Wagelaar <vincent@ricardis.tudelft.nl> *
+ * Jonas Widarsson <jonas@widarsson.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef LOCALEFLOATVALIDATOR_H
+#define LOCALEFLOATVALIDATOR_H
+
+#include <qvalidator.h>
+class QRegExp;
+namespace kt{
+ /**
+ * @author Jonas Widarsson
+ *
+ * A float validator that respects KLocale::decimalSymbol()
+ */
+ class LocaleFloatValidator : public QValidator
+ {
+ Q_OBJECT
+ public:
+ LocaleFloatValidator( QObject * parent, const char *name = 0 );
+
+ virtual QValidator::State validate( QString & str, int & pos) const;
+ private:
+ QRegExp regexp;
+ };
+}
+
+#endif
diff --git a/plugins/infowidget/peerview.cpp b/plugins/infowidget/peerview.cpp
new file mode 100644
index 0000000..0e05791
--- /dev/null
+++ b/plugins/infowidget/peerview.cpp
@@ -0,0 +1,357 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson <joris.guisson@gmail.com> *
+ * Copyright (C) 2007 by Modestas Vainius <modestas@vainius.eu> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <ksocketaddress.h>
+#include <qpoint.h>
+#include <qlistview.h>
+#include <kpopupmenu.h>
+#include <interfaces/peerinterface.h>
+#include <interfaces/functions.h>
+#include <torrent/ipblocklist.h>
+#include <util/log.h>
+#include "config.h"
+
+#ifdef USE_SYSTEM_GEOIP
+#include <GeoIP.h>
+#else
+#include "GeoIP.h"
+#endif
+#include "peerview.h"
+#include "flagdb.h"
+
+using namespace bt;
+using namespace kt;
+
+namespace kt
+{
+
+ Uint32 PeerViewItem::pvi_count = 0;
+ // Global GeoIP pointer, gets destroyed when no PeerViewItem's exist
+ static GeoIP* geo_ip = 0;
+ static QPixmap yes_pix;
+ static QPixmap no_pix;
+ static QPixmap lock_pix;
+ static FlagDB flagDB(22, 18);
+ static bool yes_no_pix_loaded = false;
+ static bool geoip_db_exists = true;
+ static QString geoip_data_file;
+
+ PeerViewItem::PeerViewItem(PeerView* pv,kt::PeerInterface* peer) : KListViewItem(pv),peer(peer)
+ {
+ if (!yes_no_pix_loaded)
+ {
+ KIconLoader* iload = KGlobal::iconLoader();
+ /* Prefer builtin flag images to the ones provided by KDE */
+ flagDB.addFlagSource("data", QString("ktorrent/geoip/%1.png"));
+ flagDB.addFlagSource("locale", QString("l10n/%1/flag.png"));
+ yes_pix = iload->loadIcon("button_ok",KIcon::Small);
+ no_pix = iload->loadIcon("button_cancel",KIcon::Small);
+ lock_pix = iload->loadIcon("ktencrypted",KIcon::Small);
+#ifdef USE_SYSTEM_GEOIP
+ geo_ip = GeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD);
+ geoip_db_exists = (geo_ip != NULL);
+#else
+ geoip_db_exists = !locate("data", "ktorrent/geoip/geoip.dat").isNull();
+ if(geoip_db_exists) {
+ geoip_data_file = "ktorrent/geoip/geoip.dat";
+ } else {
+ geoip_db_exists = !locate("data", "ktorrent/geoip/GeoIP.dat").isNull();
+ if (geoip_db_exists)
+ geoip_data_file = "ktorrent/geoip/GeoIP.dat";
+ }
+#endif
+ yes_no_pix_loaded = true;
+ }
+
+ pvi_count++;
+ const char * hostname = 0;
+ const char * country_code = 0;
+ const char * country_name = 0;
+ int country_id = 0;
+
+ const PeerInterface::Stats & s = peer->getStats();
+ hostname = s.ip_address.ascii();
+
+ // open GeoIP if necessary
+ if (!geo_ip && geoip_db_exists) {
+#ifdef USE_SYSTEM_GEOIP
+ geo_ip = GeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD);
+#else
+ geo_ip = GeoIP_open(locate("data", geoip_data_file).ascii(),0);
+#endif
+ }
+ if (geo_ip)
+ {
+ country_id = GeoIP_id_by_name(geo_ip, hostname);
+ country_code = GeoIP_country_code[country_id];
+ country_name = GeoIP_country_name[country_id];
+ setText(1, country_name);
+ m_country = QString(country_name);
+ }
+ else
+ {
+ setText(1,"N/A");
+ }
+
+ /* if (s.fast_extensions)
+ setText(0,s.ip_address + " (F)");
+ else*/
+ setText(0,s.ip_address);
+
+ struct in_addr addr = {0};
+ inet_aton(s.ip_address.ascii(),&addr);
+ ip = ntohl(addr.s_addr);
+
+ setText(2,s.client);
+
+ if (country_code)
+ {
+ setPixmap(1, flagDB.getFlag(country_code));
+ }
+
+ if (s.encrypted)
+ setPixmap(0,lock_pix);
+ update();
+ }
+
+ PeerViewItem::~PeerViewItem()
+ {
+ if (pvi_count > 0) // just to be sure, let this not wrap around
+ pvi_count--;
+
+ // destroy when not needed anymore
+ if (pvi_count == 0 && geo_ip)
+ {
+ GeoIP_delete(geo_ip);
+ geo_ip = 0;
+ }
+ }
+
+
+ void PeerViewItem::update()
+ {
+ KLocale* loc = KGlobal::locale();
+ const PeerInterface::Stats & s = peer->getStats();
+
+ if (s.download_rate >= 103) // lowest "visible" speed, all below will be 0,0 Kb/s
+ setText(3,KBytesPerSecToString(s.download_rate / 1024.0));
+ else
+ setText(3, "");
+ if (s.upload_rate >= 103) // lowest "visible" speed, all below will be 0,0 Kb/s
+ setText(4,KBytesPerSecToString(s.upload_rate / 1024.0));
+ else
+ setText(4, "");
+ //setPixmap(5,!s.choked ? yes_pix : no_pix);
+ setText(5,s.choked ? i18n("Yes") : i18n("No"));
+ //setPixmap(6,!s.snubbed ? yes_pix : no_pix);
+ setText(6,s.snubbed ? i18n("Yes") : i18n("No"));
+ setText(7,QString("%1 %").arg(loc->formatNumber(s.perc_of_file,2)));
+ setPixmap(8,s.dht_support ? yes_pix : no_pix);
+ setText(9,loc->formatNumber(s.aca_score,2));
+ setPixmap(10,s.has_upload_slot ? yes_pix : QPixmap());
+ setText(11,QString("%1 / %2").arg(s.num_down_requests).arg(s.num_up_requests));
+ setText(12, BytesToString(s.bytes_downloaded));
+ setText(13, BytesToString(s.bytes_uploaded));
+ }
+
+ int PeerViewItem::compare(QListViewItem * i,int col,bool) const
+ {
+ PeerViewItem* pvi = (PeerViewItem*) i;
+ PeerInterface* op = pvi->peer;
+ const PeerInterface::Stats & s = peer->getStats();
+ const PeerInterface::Stats & os = op->getStats();
+ switch (col)
+ {
+ case 0: return CompareVal(ip,pvi->ip); // use numeric representation to sort
+ //return QString::compare(s.ip_address,os.ip_address);
+ case 1: return QString::compare(m_country, pvi->m_country);
+ case 2: return QString::compare(s.client,os.client);
+ case 3: return CompareVal(s.download_rate,os.download_rate);
+ case 4: return CompareVal(s.upload_rate,os.upload_rate);
+ case 5: return CompareVal(s.choked,os.choked);
+ case 6: return CompareVal(s.snubbed,os.snubbed);
+ case 7: return CompareVal(s.perc_of_file,os.perc_of_file);
+ case 8: return CompareVal(s.dht_support,os.dht_support);
+ case 9: return CompareVal(s.aca_score,os.aca_score);
+ case 10: return CompareVal(s.has_upload_slot,os.has_upload_slot);
+ case 11: return CompareVal(s.num_down_requests+s.num_up_requests, os.num_down_requests+os.num_up_requests);
+ case 12: return CompareVal(s.bytes_downloaded, os.bytes_downloaded);
+ case 13: return CompareVal(s.bytes_uploaded, os.bytes_uploaded);
+
+ }
+ return 0;
+ }
+
+ PeerView::PeerView(QWidget *parent, const char *name)
+ : KListView(parent, name)
+ {
+ addColumn(i18n("IP"));
+ addColumn(i18n("Country"));
+ addColumn(i18n("Client"));
+ addColumn(i18n("Down Speed"));
+ addColumn(i18n("Up Speed"));
+ addColumn(i18n("Choked"));
+ addColumn(i18n("Snubbed"));
+ addColumn(i18n("Availability"));
+ addColumn(i18n("DHT"));
+ addColumn(i18n("Score"));
+ addColumn(i18n("Upload Slot"));
+ addColumn(i18n("Requests"));
+ addColumn(i18n("Downloaded"));
+ addColumn(i18n("Uploaded"));
+
+ setAllColumnsShowFocus(true);
+ setShowSortIndicator(true);
+
+ setColumnAlignment(3,Qt::AlignRight);
+ setColumnAlignment(4,Qt::AlignRight);
+ setColumnAlignment(5,Qt::AlignCenter);
+ setColumnAlignment(6,Qt::AlignCenter);
+ setColumnAlignment(7,Qt::AlignRight);
+ setColumnAlignment(8,Qt::AlignCenter);
+ setColumnAlignment(9,Qt::AlignRight);
+ setColumnAlignment(10,Qt::AlignCenter);
+ setColumnAlignment(11,Qt::AlignCenter);
+ setColumnAlignment(12,Qt::AlignRight);
+ setColumnAlignment(13,Qt::AlignRight);
+
+ for (Uint32 i = 0;i < (Uint32)columns();i++)
+ setColumnWidthMode(i,QListView::Manual);
+
+ setShowSortIndicator(true);
+
+ menu = new KPopupMenu(this);
+ kick_id = menu->insertItem(KGlobal::iconLoader()->loadIcon("delete_user", KIcon::NoGroup), i18n("to kick", "Kick peer"));
+ ban_id = menu->insertItem(KGlobal::iconLoader()->loadIcon("filter",KIcon::NoGroup), i18n("to ban", "Ban peer"));
+
+ connect(this,SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint& )),
+ this,SLOT(showContextMenu(KListView*, QListViewItem*, const QPoint& )));
+ connect(menu, SIGNAL ( activated ( int ) ), this, SLOT ( contextItem ( int ) ) );
+ setFrameShape(QFrame::NoFrame);
+ }
+
+
+ PeerView::~PeerView()
+ {}
+
+ void PeerView::addPeer(kt::PeerInterface* peer)
+ {
+ PeerViewItem* i = new PeerViewItem(this,peer);
+ items.insert(peer,i);
+ }
+
+ void PeerView::removePeer(kt::PeerInterface* peer)
+ {
+ QMap<kt::PeerInterface*,PeerViewItem*>::iterator it = items.find(peer);
+ if (it == items.end())
+ {
+ return;
+ }
+
+ PeerViewItem* pvi = it.data();
+ if (pvi == curr)
+ curr = 0;
+
+ delete pvi;
+ items.erase(peer);
+
+ }
+
+ void PeerView::banPeer(kt::PeerInterface* peer)
+ {
+ if(!peer)
+ return;
+
+ IPBlocklist& filter = IPBlocklist::instance();
+ const PeerInterface::Stats & s = peer->getStats();
+ KNetwork::KIpAddress ip(s.ip_address);
+ QString ips = ip.toString();
+ /**
+ * @TODO Clean this up.
+ * this whole mess was because of KNetwork classes
+ * since we no longer use them, may I clean it up?
+ * I'll wait some time just in case...
+ **/
+ if(ips.startsWith(":"))
+ filter.insert(ips.section(":",-1),3);
+ else
+ filter.insert(ips,3);
+ peer->kill();
+ }
+
+ void PeerView::kickPeer(kt::PeerInterface* peer)
+ {
+ if(!peer)
+ return;
+
+ peer->kill();
+ }
+
+ void PeerView::update()
+ {
+ QMap<kt::PeerInterface*,PeerViewItem*>::iterator i = items.begin();
+ while (i != items.end())
+ {
+ PeerViewItem* it = i.data();
+ it->update();
+ i++;
+ }
+ sort();
+ }
+
+ void PeerView::removeAll()
+ {
+ items.clear();
+ clear();
+ }
+
+ void PeerView::showContextMenu( KListView*, QListViewItem* item, const QPoint& p)
+ {
+ if(!item)
+ return;
+
+ curr = dynamic_cast<PeerViewItem*>(item);
+ if (curr)
+ {
+ menu->setItemEnabled(ban_id, true);
+ menu->setItemEnabled(kick_id, true);
+ menu->popup(p);
+ }
+ }
+
+ void PeerView::contextItem(int id)
+ {
+ if (id == ban_id && curr)
+ banPeer(curr->getPeer());
+
+ if (id == kick_id && curr)
+ kickPeer(curr->getPeer());
+ }
+}
+
+#include "peerview.moc"
diff --git a/plugins/infowidget/peerview.h b/plugins/infowidget/peerview.h
new file mode 100644
index 0000000..a4a8b94
--- /dev/null
+++ b/plugins/infowidget/peerview.h
@@ -0,0 +1,81 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef PEERVIEW_H
+#define PEERVIEW_H
+
+#include <qmap.h>
+#include <klistview.h>
+#include <qlistview.h>
+#include <kpopupmenu.h>
+#include <qpoint.h>
+#include <util/constants.h>
+
+namespace kt
+{
+ class PeerInterface;
+ class PeerView;
+
+ class PeerViewItem : public KListViewItem
+ {
+ kt::PeerInterface* peer;
+ QString m_country;
+ bt::Uint32 ip;
+ // counter to keep track of how many PeerViewItem objects are in existence
+ static bt::Uint32 pvi_count;
+ public:
+ PeerViewItem(PeerView* pv,kt::PeerInterface* peer);
+ virtual ~PeerViewItem();
+
+ void update();
+ int compare(QListViewItem * i,int col,bool) const;
+ kt::PeerInterface* getPeer() { return peer; }
+
+ };
+
+ /**
+ @author Joris Guisson
+ */
+ class PeerView : public KListView
+ {
+ Q_OBJECT
+
+ QMap<kt::PeerInterface*,PeerViewItem*> items;
+ public:
+ PeerView(QWidget *parent = 0, const char *name = 0);
+ virtual ~PeerView();
+
+ public slots:
+ void addPeer(kt::PeerInterface* peer);
+ void removePeer(kt::PeerInterface* peer);
+ void banPeer(kt::PeerInterface* peer);
+ void kickPeer(kt::PeerInterface* peer);
+ void update();
+ void removeAll();
+ void showContextMenu(KListView* ,QListViewItem* item,const QPoint & p);
+ void contextItem(int id);
+ private:
+ KPopupMenu* menu;
+ int ban_id;
+ int kick_id;
+ PeerViewItem* curr;
+ };
+}
+
+#endif
diff --git a/plugins/infowidget/statustab.cpp b/plugins/infowidget/statustab.cpp
new file mode 100644
index 0000000..b0974ed
--- /dev/null
+++ b/plugins/infowidget/statustab.cpp
@@ -0,0 +1,267 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <math.h>
+#include <float.h>
+#include <qdatetime.h>
+#include <qcheckbox.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <interfaces/functions.h>
+#include <interfaces/torrentinterface.h>
+
+#include "downloadedchunkbar.h"
+#include "availabilitychunkbar.h"
+#include "floatspinbox.h"
+#include "statustab.h"
+
+namespace kt
+{
+
+ StatusTab::StatusTab(QWidget* parent, const char* name, WFlags fl)
+ : StatusTabBase(parent,name,fl),curr_tc(0)
+ {
+ QColorGroup cg = colorGroup();
+ // do not use hardcoded colors
+ m_info_caption->setPaletteBackgroundColor(cg.mid());
+ m_chunks_caption->setPaletteBackgroundColor(cg.mid());
+ m_sharing_caption->setPaletteBackgroundColor(cg.mid());
+
+ maxRatio->setMinValue(0.0f);
+ maxRatio->setMaxValue(100.0f);
+ maxRatio->setStep(0.1f);
+ connect(maxRatio, SIGNAL(valueHasChanged()), this, SLOT(maxRatioReturnPressed()));
+ connect(useLimit, SIGNAL( toggled(bool) ), this, SLOT( useLimitToggled(bool) ) );
+
+ maxTime->setMinValue(0.0f);
+ maxTime->setMaxValue(10000000.0f);
+ maxTime->setStep(0.05f);
+ maxTime->setSpecialValueText(i18n("No limit"));
+ connect(useTimeLimit,SIGNAL(toggled(bool)), this,SLOT(useTimeLimitToggled(bool)));
+ connect(maxTime,SIGNAL(valueHasChanged()), this, SLOT(timeValueChanged()));
+
+ int h = (int)ceil(fontMetrics().height()*1.25);
+ m_chunk_bar->setFixedHeight(h);
+ m_av_chunk_bar->setFixedHeight(h);
+ }
+
+ StatusTab::~StatusTab()
+ {}
+
+ void StatusTab::changeTC(kt::TorrentInterface* tc)
+ {
+ if (tc == curr_tc)
+ return;
+
+ curr_tc = tc;
+
+ m_chunk_bar->setTC(tc);
+ m_av_chunk_bar->setTC(tc);
+ setEnabled(tc != 0);
+
+ if (curr_tc)
+ {
+ float ratio = curr_tc->getMaxShareRatio();
+ if(ratio > 0)
+ {
+ useLimit->setChecked(true);
+ maxRatio->setValue(ratio);
+ }
+ else
+ {
+ maxRatio->setValue(0.0);
+ useLimit->setChecked(false);
+ maxRatio->setEnabled(false);
+ }
+
+ float hours = curr_tc->getMaxSeedTime();
+ if (hours > 0)
+ {
+ maxTime->setEnabled(true);
+ useTimeLimit->setChecked(true);
+ maxTime->setValue(hours);
+ }
+ else
+ {
+ maxTime->setEnabled(false);
+ useTimeLimit->setChecked(false);
+ }
+ }
+ else
+ {
+ maxRatio->setValue(0.00f);
+ m_share_ratio->clear();
+ m_tracker_status->clear();
+ m_seeders->clear();
+ m_leechers->clear();
+ m_tracker_update_time->clear();
+ m_avg_up->clear();
+ m_avg_down->clear();
+ }
+
+ update();
+ }
+
+ void StatusTab::update()
+ {
+ if (!curr_tc)
+ return;
+
+ const TorrentStats & s = curr_tc->getStats();
+
+ m_chunk_bar->updateBar();
+ m_av_chunk_bar->updateBar();
+
+ if (s.running)
+ {
+ QTime t;
+ t = t.addSecs(curr_tc->getTimeToNextTrackerUpdate());
+ m_tracker_update_time->setText(t.toString("mm:ss"));
+ }
+ else
+ {
+ m_tracker_update_time->setText("");
+ }
+
+ m_tracker_status->setText(s.trackerstatus);
+
+ m_seeders->setText(QString("%1 (%2)")
+ .arg(s.seeders_connected_to).arg(s.seeders_total));
+
+ m_leechers->setText(QString("%1 (%2)")
+ .arg(s.leechers_connected_to).arg(s.leechers_total));
+
+ float ratio = kt::ShareRatio(s);
+ if(!maxRatio->hasFocus() && useLimit->isChecked())
+ maxRatioUpdate();
+
+ m_share_ratio->setText(QString("<font color=\"%1\">%2</font>").arg(ratio <= 0.8 ? "#ff0000" : "#1c9a1c").arg(KGlobal::locale()->formatNumber(ratio,2)));
+
+ Uint32 secs = curr_tc->getRunningTimeUL();
+ if (secs == 0)
+ {
+ m_avg_up->setText(KBytesPerSecToString(0));
+
+ }
+ else
+ {
+ double r = (double)s.bytes_uploaded / 1024.0;
+ m_avg_up->setText(KBytesPerSecToString(r / secs));
+ }
+
+ secs = curr_tc->getRunningTimeDL();
+ if (secs == 0)
+ {
+ m_avg_down->setText(KBytesPerSecToString(0));
+ }
+ else
+ {
+ double r = (double)(s.bytes_downloaded - s.imported_bytes)/ 1024.0;
+ m_avg_down->setText(KBytesPerSecToString(r / secs));
+ }
+ }
+
+ void StatusTab::maxRatioReturnPressed()
+ {
+ if(!curr_tc)
+ return;
+
+ curr_tc->setMaxShareRatio(maxRatio->value());
+ }
+
+ void StatusTab::useLimitToggled(bool state)
+ {
+ if(!curr_tc)
+ return;
+
+ maxRatio->setEnabled(state);
+ if (!state)
+ {
+ curr_tc->setMaxShareRatio(0.00f);
+ maxRatio->setValue(0.00f);
+ }
+ else
+ {
+ float msr = curr_tc->getMaxShareRatio();
+ if(msr == 0.00f)
+ {
+ curr_tc->setMaxShareRatio(1.00f);
+ maxRatio->setValue(1.00f);
+ }
+
+ float sr = kt::ShareRatio(curr_tc->getStats());
+ if(sr >= 1.00f)
+ {
+ //always add 1 to max share ratio to prevent stopping if torrent is running.
+ curr_tc->setMaxShareRatio(sr + 1.00f);
+ maxRatio->setValue(sr + 1.00f);
+ }
+ }
+ }
+
+ void StatusTab::maxRatioUpdate()
+ {
+ if(!curr_tc)
+ return;
+
+ float ratio = curr_tc->getMaxShareRatio();
+ if(ratio > 0.00f)
+ {
+ maxRatio->setEnabled(true);
+ useLimit->setChecked(true);
+ maxRatio->setValue(ratio);
+ }
+ else
+ {
+ maxRatio->setEnabled(false);
+ useLimit->setChecked(false);
+ maxRatio->setValue(0.00f);
+ }
+ }
+
+ void StatusTab::useTimeLimitToggled(bool on)
+ {
+ if(!curr_tc)
+ return;
+
+ maxTime->setEnabled(on);
+ if (on)
+ {
+ Uint32 dl = curr_tc->getRunningTimeDL();
+ Uint32 ul = curr_tc->getRunningTimeUL();
+ float hours = (ul - dl) / 3600.0f + 1.0; // add one hour to current seed time to not stop immediatly
+ maxTime->setValue(hours);
+ curr_tc->setMaxSeedTime(hours);
+ }
+ else
+ {
+ curr_tc->setMaxSeedTime(0.0f);
+ }
+ }
+
+ void StatusTab::timeValueChanged()
+ {
+ if (curr_tc)
+ curr_tc->setMaxSeedTime(maxTime->value());
+ }
+
+}
+
+#include "statustab.moc"
+
diff --git a/plugins/infowidget/statustab.h b/plugins/infowidget/statustab.h
new file mode 100644
index 0000000..14e987d
--- /dev/null
+++ b/plugins/infowidget/statustab.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef STATUSTAB_H
+#define STATUSTAB_H
+
+#include "statustabbase.h"
+
+namespace kt
+{
+ class TorrentInterface;
+
+ class StatusTab : public StatusTabBase
+ {
+ Q_OBJECT
+
+ public:
+ StatusTab(QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
+ virtual ~StatusTab();
+
+
+ public slots:
+ void changeTC(kt::TorrentInterface* tc);
+ void update();
+ void maxRatioReturnPressed();
+ void useLimitToggled(bool on);
+ void useTimeLimitToggled(bool on);
+ void timeValueChanged();
+ private:
+ void maxRatioUpdate();
+
+ private:
+ kt::TorrentInterface* curr_tc;
+ };
+}
+
+#endif
+
diff --git a/plugins/infowidget/statustabbase.ui b/plugins/infowidget/statustabbase.ui
new file mode 100644
index 0000000..179f68a
--- /dev/null
+++ b/plugins/infowidget/statustabbase.ui
@@ -0,0 +1,667 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>StatusTabBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>StatusTabBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>839</width>
+ <height>250</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Status</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_info_caption</cstring>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>200</red>
+ <green>200</green>
+ <blue>200</blue>
+ </color>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Info</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout15</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Seeders:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_6</cstring>
+ </property>
+ <property name="text">
+ <string>Leechers:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_seeders</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_leechers</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Average Down Speed:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_avg_down</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_avg_up</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_7</cstring>
+ </property>
+ <property name="text">
+ <string>Average Up Speed:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Next update in:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_5</cstring>
+ </property>
+ <property name="text">
+ <string>Tracker Status:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_tracker_status</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>m_tracker_update_time</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>207</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout17</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout23</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout25</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_chunks_caption</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>200</red>
+ <green>200</green>
+ <blue>200</blue>
+ </color>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Chunks</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Parts of the torrent which have been downloaded:</string>
+ </property>
+ </widget>
+ <widget class="kt::DownloadedChunkBar">
+ <property name="name">
+ <cstring>m_chunk_bar</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Parts of the torrent which are available:</string>
+ </property>
+ </widget>
+ <widget class="kt::AvailabilityChunkBar">
+ <property name="name">
+ <cstring>m_av_chunk_bar</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>VLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_sharing_caption</cstring>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>200</red>
+ <green>200</green>
+ <blue>200</blue>
+ </color>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Sharing</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout15</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="kt::FloatSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>maxRatio</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Your upload will stop when share ratio gets to this value. Zero means no limit.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel3_3</cstring>
+ </property>
+ <property name="text">
+ <string>Share ratio:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="1">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="kt::FloatSpinBox">
+ <property name="name">
+ <cstring>maxTime</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Your upload will stop when you have uploaded for this many hours.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_9</cstring>
+ </property>
+ <property name="text">
+ <string>Hours</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>useLimit</cstring>
+ </property>
+ <property name="text">
+ <string>Ratio limit:</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Limit works only in seed mode</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>useTimeLimit</cstring>
+ </property>
+ <property name="text">
+ <string>Time &amp;limit:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_share_ratio</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>kt::DownloadedChunkBar</class>
+ <header location="local">downloadedchunkbar.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>20</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+ <customwidget>
+ <class>kt::AvailabilityChunkBar</class>
+ <header location="local">availabilitychunkbar.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>20</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+ <customwidget>
+ <class>kt::FloatSpinBox</class>
+ <header location="local">floatspinbox.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>downloadedchunkbar.h</includehint>
+ <includehint>availabilitychunkbar.h</includehint>
+ <includehint>floatspinbox.h</includehint>
+ <includehint>floatspinbox.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/infowidget/trackerview.cpp b/plugins/infowidget/trackerview.cpp
new file mode 100644
index 0000000..2339189
--- /dev/null
+++ b/plugins/infowidget/trackerview.cpp
@@ -0,0 +1,243 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "trackerview.h"
+#include <interfaces/torrentinterface.h>
+#include <interfaces/trackerslist.h>
+
+#include <qdatetime.h>
+#include <qstring.h>
+#include <qlabel.h>
+#include <qlistview.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+
+#include <klocale.h>
+#include <kurl.h>
+#include <kmessagebox.h>
+#include <kpushbutton.h>
+#include <ksqueezedtextlabel.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klistview.h>
+
+#include <torrent/globals.h>
+#include <util/log.h>
+
+namespace kt
+{
+ TrackerView::TrackerView(QWidget *parent, const char *name)
+ :TrackerViewBase(parent, name), tc(0)
+ {
+ KIconLoader* iload = KGlobal::iconLoader();
+ btnUpdate->setIconSet(iload->loadIconSet("apply", KIcon::Small));
+ btnAdd->setIconSet(iload->loadIconSet("add", KIcon::Small));
+ btnRemove->setIconSet(iload->loadIconSet("remove", KIcon::Small));
+ btnRestore->setIconSet(iload->loadIconSet("undo", KIcon::Small));
+
+ QPalette p = lblCurrent->palette();
+ p.setColor(QPalette::Active,QColorGroup::Base,p.color(QPalette::Active,QColorGroup::Background));
+ lblCurrent->setPalette(p);
+ }
+
+ TrackerView::~TrackerView()
+ {
+ }
+
+ void TrackerView::btnAdd_clicked()
+ {
+ if(!tc || txtTracker->text().isEmpty())
+ return;
+
+ if(tc->getStats().priv_torrent)
+ {
+ KMessageBox::sorry(0, i18n("Cannot add a tracker to a private torrent."));
+ return;
+ }
+
+ KURL url(txtTracker->text());
+ if(!url.isValid())
+ {
+ KMessageBox::error(0, i18n("Malformed URL."));
+ return;
+ }
+
+ new QListViewItem(listTrackers, txtTracker->text());
+ tc->getTrackersList()->addTracker(url,true);
+ }
+
+ void TrackerView::btnRemove_clicked()
+ {
+ QListViewItem* current = listTrackers->currentItem();
+ if(!current)
+ return;
+
+ KURL url(current->text(0));
+ if(tc->getTrackersList()->removeTracker(url))
+ delete current;
+ else
+ KMessageBox::sorry(0, i18n("Cannot remove torrent default tracker."));
+ }
+
+ void TrackerView::btnChange_clicked()
+ {
+ QListViewItem* current = listTrackers->currentItem();
+ if(!current)
+ return;
+
+ KURL url(current->text(0));
+ tc->getTrackersList()->setTracker(url);
+ tc->updateTracker();
+ }
+
+ void TrackerView::btnRestore_clicked()
+ {
+ tc->getTrackersList()->restoreDefault();
+ tc->updateTracker();
+
+ // update the list of trackers
+ listTrackers->clear();
+
+ const KURL::List trackers = tc->getTrackersList()->getTrackerURLs();
+ if(trackers.empty())
+ return;
+
+ for (KURL::List::const_iterator i = trackers.begin();i != trackers.end();i++)
+ new QListViewItem(listTrackers, (*i).prettyURL());
+ }
+
+ void TrackerView::btnUpdate_clicked()
+ {
+ if(!tc)
+ return;
+
+ tc->updateTracker();
+ }
+
+ void TrackerView::listTrackers_currentChanged(QListViewItem* item)
+ {
+ if(!item)
+ txtTracker->clear();
+ else
+ txtTracker->setText(item->text(0));
+ }
+
+ void TrackerView::changeTC(TorrentInterface* ti)
+ {
+ if (tc == ti)
+ return;
+
+ setEnabled(ti != 0);
+ torrentChanged(ti);
+ update();
+ }
+
+ void TrackerView::update()
+ {
+ if(!tc)
+ return;
+
+ const TorrentStats & s = tc->getStats();
+ if (s.running)
+ {
+ QTime t;
+ t = t.addSecs(tc->getTimeToNextTrackerUpdate());
+ lblUpdate->setText(t.toString("mm:ss"));
+ }
+
+ //Update manual annunce button
+ btnUpdate->setEnabled(s.running && tc->announceAllowed());
+ // only enable change when we can actually change and the torrent is running
+ btnChange->setEnabled(s.running && listTrackers->childCount() > 1);
+
+ lblStatus->setText("<b>" + s.trackerstatus + "</b>");
+ if (tc->getTrackersList())
+ {
+ QString t = tc->getTrackersList()->getTrackerURL().prettyURL();
+ if (lblCurrent->text() != t )
+ lblCurrent->setText(t);
+ }
+ else
+ lblCurrent->clear();
+
+ btnAdd->setEnabled(txtTracker->text() != QString::null && !tc->getStats().priv_torrent);
+ }
+
+ void TrackerView::onLoadingFinished(const KURL & ,bool,bool)
+ {
+ torrentChanged(tc);
+ }
+
+ void TrackerView::torrentChanged(TorrentInterface* ti)
+ {
+ tc = ti;
+ listTrackers->clear();
+ if(!tc)
+ {
+ lblStatus->clear();
+ lblCurrent->clear();
+ lblUpdate->clear();
+ txtTracker->clear();
+
+ btnAdd->setEnabled(false);
+ btnRemove->setEnabled(false);
+ btnRestore->setEnabled(false);
+ btnChange->setEnabled(false);
+ btnRestore->setEnabled(false);
+ return;
+ }
+
+ const TorrentStats & s = tc->getStats();
+
+ if (s.priv_torrent)
+ {
+ btnAdd->setEnabled(false);
+ btnRemove->setEnabled(false);
+ btnRestore->setEnabled(false);
+ txtTracker->setText(i18n("You cannot add trackers to a private torrent"));
+ txtTracker->setEnabled(false);
+ }
+ else
+ {
+ btnAdd->setEnabled(true);
+ btnRemove->setEnabled(true);
+ btnRestore->setEnabled(true);
+ txtTracker->clear();
+ txtTracker->setEnabled(true);
+ }
+
+ const KURL::List trackers = tc->getTrackersList()->getTrackerURLs();
+ if(trackers.empty())
+ {
+ new QListViewItem(listTrackers, tc->getTrackersList()->getTrackerURL().prettyURL());
+ }
+ else
+ {
+ for (KURL::List::const_iterator i = trackers.begin();i != trackers.end();i++)
+ new QListViewItem(listTrackers, (*i).prettyURL());
+ }
+
+ btnUpdate->setEnabled(s.running && tc->announceAllowed());
+ btnChange->setEnabled(s.running && listTrackers->childCount() > 1);
+ }
+}
+
+
+#include "trackerview.moc"
diff --git a/plugins/infowidget/trackerview.h b/plugins/infowidget/trackerview.h
new file mode 100644
index 0000000..fd23fb8
--- /dev/null
+++ b/plugins/infowidget/trackerview.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Joris Guisson, Ivan Vasic *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef TRACKERVIEW_H
+#define TRACKERVIEW_H
+
+#include <kurl.h>
+#include "trackerviewbase.h"
+
+namespace kt
+{
+ class TorrentInterface;
+ class TorrentFileInterface;
+
+ /**
+ * @author Ivan Vasic <ivan@ktorrent.org>
+ */
+ class TrackerView: public TrackerViewBase
+ {
+ Q_OBJECT
+ public:
+ TrackerView(QWidget *parent = 0, const char *name = 0);
+ virtual ~TrackerView();
+
+ void update();
+ void changeTC(TorrentInterface* ti);
+
+ public slots:
+ virtual void btnUpdate_clicked();
+ virtual void btnRestore_clicked();
+ virtual void btnChange_clicked();
+ virtual void btnRemove_clicked();
+ virtual void btnAdd_clicked();
+ virtual void listTrackers_currentChanged(QListViewItem*);
+ void onLoadingFinished(const KURL & ,bool,bool);
+
+ private:
+ void torrentChanged(TorrentInterface* ti);
+
+ private:
+ TorrentInterface* tc;
+
+ };
+}
+#endif
diff --git a/plugins/infowidget/trackerviewbase.ui b/plugins/infowidget/trackerviewbase.ui
new file mode 100644
index 0000000..758ddc7
--- /dev/null
+++ b/plugins/infowidget/trackerviewbase.ui
@@ -0,0 +1,317 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>TrackerViewBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>TrackerViewBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>750</width>
+ <height>254</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="0">
+ <property name="name">
+ <cstring>txtTracker</cstring>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>btnUpdate</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Update Tracker</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Minimum update interval - 60 seconds</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>btnAdd</cstring>
+ </property>
+ <property name="text">
+ <string>Add Trac&amp;ker</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="1">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnRemove</cstring>
+ </property>
+ <property name="text">
+ <string>Remove Tracker</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnChange</cstring>
+ </property>
+ <property name="text">
+ <string>Ch&amp;ange Tracker</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>68</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnRestore</cstring>
+ </property>
+ <property name="text">
+ <string>Restore Defaults</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="KListView" row="2" column="0">
+ <column>
+ <property name="text">
+ <string>Trackers</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>listTrackers</cstring>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>URL:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>lblCurrent</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Status:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblStatus</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Next update in:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblUpdate</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>btnChange</sender>
+ <signal>clicked()</signal>
+ <receiver>TrackerViewBase</receiver>
+ <slot>btnChange_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnUpdate</sender>
+ <signal>clicked()</signal>
+ <receiver>TrackerViewBase</receiver>
+ <slot>btnUpdate_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnAdd</sender>
+ <signal>clicked()</signal>
+ <receiver>TrackerViewBase</receiver>
+ <slot>btnAdd_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnRemove</sender>
+ <signal>clicked()</signal>
+ <receiver>TrackerViewBase</receiver>
+ <slot>btnRemove_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnRestore</sender>
+ <signal>clicked()</signal>
+ <receiver>TrackerViewBase</receiver>
+ <slot>btnRestore_clicked()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>btnChange_clicked()</slot>
+ <slot>listTrackers_currentChanged(QListViewItem*)</slot>
+ <slot>btnUpdate_clicked()</slot>
+ <slot>btnAdd_clicked()</slot>
+ <slot>btnRemove_clicked()</slot>
+ <slot>btnRestore_clicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/ipfilter/Makefile.am b/plugins/ipfilter/Makefile.am
new file mode 100644
index 0000000..02b193a
--- /dev/null
+++ b/plugins/ipfilter/Makefile.am
@@ -0,0 +1,31 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+kde_module_LTLIBRARIES = ktipfilterplugin.la
+noinst_HEADERS = ipfilterplugin.h ipblockingprefpage.h antip2p.h \
+ convertdialog.h
+ktipfilterplugin_la_SOURCES = ipfilterplugin.cpp ipblockingpref.ui \
+ ipblockingprefpage.cpp ipfilterpluginsettings.kcfgc antip2p.cpp convert_dlg.ui \
+ convertdialog.cpp
+
+# Libs needed by the plugin
+ktipfilterplugin_la_LIBADD = $(LIB_KHTML) $(LIB_KPARTS) \
+ ../../libktorrent/libktorrent.la $(LIB_QT) \
+ $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KFILE)
+
+
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktipfilterplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+# rc file containing the GUI for the plugin
+# pluginsdir = $(kde_datadir)/ktsearchplugin
+# plugins_DATA = ktsearchpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+kde_services_DATA = ktipfilterplugin.desktop
+
+kde_kcfg_DATA = ktipfilterplugin.kcfg
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/ipfilter/antip2p.cpp b/plugins/ipfilter/antip2p.cpp
new file mode 100644
index 0000000..28f9b24
--- /dev/null
+++ b/plugins/ipfilter/antip2p.cpp
@@ -0,0 +1,237 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "antip2p.h"
+
+#include <torrent/globals.h>
+#include <util/log.h>
+#include <util/constants.h>
+#include <util/mmapfile.h>
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+#include <qstring.h>
+#include <qvaluelist.h>
+
+using namespace bt;
+
+namespace kt
+{
+
+ bool IPBlock::operator < (const IPBlock & b) const
+ {
+ if (ip2 < b.ip1) // a range is before b range
+ return true;
+ else if (b.ip2 < ip1) // b range is before a range
+ return false;
+ else
+ return ip1 < b.ip1;// a and b intersect
+ }
+
+ Uint32 AntiP2P::toUint32(const QString& ip)
+ {
+ bool test;
+ Uint32 ret = ip.section('.',0,0).toULongLong(&test);
+ ret <<= 8;
+ ret |= ip.section('.',1,1).toULong(&test);
+ ret <<= 8;
+ ret |= ip.section('.',2,2).toULong(&test);
+ ret <<= 8;
+ ret |= ip.section('.',3,3).toULong(&test);
+
+ return ret;
+ }
+
+ QString fromUint32(Uint32 ip)
+ {
+ Uint32 tmp = ip;
+ QString out;
+
+ tmp = ip;
+ tmp &= 0x000000FF;
+ out.prepend(QString("%1").arg(tmp));
+ ip >>= 8;
+ tmp = ip;
+ tmp &= 0x000000FF;
+ out.prepend(QString("%1.").arg(tmp));
+ ip >>= 8;
+ tmp = ip;
+ tmp &= 0x000000FF;
+ out.prepend(QString("%1.").arg(tmp));
+ ip >>= 8;
+ tmp = ip;
+ tmp &= 0x000000FF;
+ out.prepend(QString("%1.").arg(tmp));
+
+ return out;
+ }
+
+ AntiP2P::AntiP2P()
+ {
+ header_loaded = false;
+ load();
+ }
+
+ AntiP2P::~AntiP2P()
+ {
+ if(file)
+ delete file;
+
+ Out(SYS_IPF|LOG_ALL) << "Anti-P2P filter unloaded." << endl;
+ }
+
+ void AntiP2P::load()
+ {
+ file = new MMapFile();
+ if(! file->open(KGlobal::dirs()->saveLocation("data","ktorrent") + "level1.dat", MMapFile::READ) )
+ {
+ Out(SYS_IPF|LOG_NOTICE) << "Anti-p2p file not loaded." << endl;
+ file = 0;
+ return;
+ }
+ Out(SYS_IPF|LOG_ALL) << "Loading Anti-P2P filter..." << endl;
+ }
+
+ void AntiP2P::loadHeader()
+ {
+ if(!file)
+ return;
+
+ Uint32 nrElements = file->getSize() / sizeof(IPBlock);
+ uint blocksize = nrElements < 100 ? 10 : 100; // number of entries that each HeaderBlock holds. If total number is < 100, than this value is 10.
+ HeaderBlock hb;
+
+ for(Uint64 i = 0; i < file->getSize() ; i+= sizeof(IPBlock)*(blocksize) )
+ {
+ IPBlock ipb;
+ hb.offset = i;
+ file->seek(MMapFile::BEGIN, i);
+ file->read(&ipb, sizeof(IPBlock));
+ hb.ip1 = ipb.ip1;
+ if ( i + (blocksize-1)*sizeof(IPBlock) > file->getSize() ) //last entry
+ {
+ file->seek(MMapFile::BEGIN, file->getSize() - sizeof(IPBlock));
+ file->read(&ipb, sizeof(IPBlock));
+ hb.ip2 = ipb.ip2;
+ hb.nrEntries = nrElements % blocksize;
+
+ blocks.push_back(hb);
+ break;
+ }
+ file->seek(MMapFile::BEGIN, i + (blocksize-1)*sizeof(IPBlock));
+ file->read(&ipb, sizeof(IPBlock));
+ hb.ip2 = ipb.ip2;
+ hb.nrEntries = blocksize;
+ blocks.push_back(hb);
+ }
+
+ Out(SYS_IPF|LOG_NOTICE) << "AntiP2P header loaded." << endl;
+ header_loaded = true;
+ }
+
+ bool AntiP2P::exists()
+ {
+ return file != 0;
+ }
+
+ bool AntiP2P::isBlockedIP(const QString& ip )
+ {
+ Uint32 test = toUint32(ip);
+ return isBlockedIP(test);
+ }
+
+ int AntiP2P::searchHeader(Uint32& ip, int start, int end)
+ {
+ if (end == 0)
+ return -1; //empty list
+
+ if (end == 1)
+ {
+ if (blocks[start].ip1 <= ip && blocks[start].ip2 >= ip) //then our IP is somewhere in between
+ {
+ if (blocks[start].ip1 == ip || blocks[start].ip2 == ip)
+ return -2; //Return -2 to signal that this IP matches either IP from header. No need to search mmaped file in that case.
+ else
+ return start; //else return block index
+ }
+ else
+ return -1; //not found
+ }
+ else
+ {
+ int i = start + end/2;
+ if (blocks[i].ip1 <= ip)
+ return searchHeader(ip, i, end - end/2);
+ else
+ return searchHeader(ip, start, end/2);
+ }
+ }
+
+ bool AntiP2P::isBlockedIP( Uint32& ip )
+ {
+ if (!header_loaded)
+ {
+ Out(SYS_IPF|LOG_IMPORTANT) << "Tried to check if IP was blocked, but no AntiP2P header was loaded." << endl;
+ return false;
+ }
+
+ int in_header = searchHeader(ip, 0, blocks.count());
+ switch (in_header)
+ {
+ case -1:
+ return false; //ip is not blocked
+ case -2:
+ return true; //ip is blocked (we're really lucky to find it in header already)
+ default:
+ //search mmapped file
+ HeaderBlock to_be_searched = blocks[in_header];
+ Uint8* fptr = (Uint8*) file->getDataPointer();
+ fptr += to_be_searched.offset;
+ IPBlock* file_blocks = (IPBlock*) fptr;
+ return searchFile(file_blocks, ip, 0, to_be_searched.nrEntries);
+ break;
+ }
+ return false;
+ }
+
+ bool AntiP2P::searchFile(IPBlock* file_blocks, Uint32& ip, int start, int end)
+ {
+ if (end == 0)
+ return false; //empty list, so not found
+
+ if (end == 1)
+ {
+ if (file_blocks[start].ip1 <= ip && file_blocks[start].ip2 >= ip) //we have a match!
+ return true;
+ else
+ return false; //IP is not found.
+ }
+
+ else
+ {
+ int i = start + end/2;
+ if (file_blocks[i].ip1 <= ip)
+ return searchFile(file_blocks, ip, i, end - end/2);
+ else
+ return searchFile(file_blocks, ip, start, end/2);
+ }
+ }
+}
diff --git a/plugins/ipfilter/antip2p.h b/plugins/ipfilter/antip2p.h
new file mode 100644
index 0000000..48350e3
--- /dev/null
+++ b/plugins/ipfilter/antip2p.h
@@ -0,0 +1,117 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef ANTIP2P_H
+#define ANTIP2P_H
+
+#include <util/mmapfile.h>
+#include <util/constants.h>
+
+#include <qvaluelist.h>
+#include <qstring.h>
+
+namespace kt
+{
+ typedef struct
+ {
+ bt::Uint32 ip1;
+ bt::Uint32 ip2;
+ bt::Uint64 offset;
+ bt::Uint32 nrEntries;
+ } HeaderBlock;
+
+ struct IPBlock
+ {
+ bt::Uint32 ip1;
+ bt::Uint32 ip2;
+
+ bool operator < (const IPBlock & b) const;
+ };
+
+ /**
+ * @author Ivan Vasic <ivasic@gmail.com>
+ * @brief This class is used to manage anti-p2p filter list, so called level1.
+ */
+ class AntiP2P
+ {
+ public:
+ AntiP2P();
+ ~AntiP2P();
+
+ /**
+ * Checks if anti-p2p file is present. Used to check if we should use level1 list
+ **/
+ bool exists();
+
+
+ /**
+ * Creates and loads the header from antip2p filter file.
+ **/
+ void loadHeader();
+
+
+ /**
+ * Checks if specified IP is listed in filter file.
+ * @return TRUE if IP should be blocked, FALSE otherwise
+ * @param ip QString representation of IP to be checked
+ **/
+ bool isBlockedIP(const QString& ip);
+
+ /**
+ * Overloaded function. Uses Uint32 IP to be checked
+ **/
+ bool isBlockedIP(bt::Uint32& ip);
+
+ /**
+ * This function converts QString IP to Uint32 format.
+ **/
+ static bt::Uint32 toUint32(const QString& ip);
+
+ private:
+ bt::MMapFile* file;
+ QValueList<HeaderBlock> blocks;
+
+ ///Is AntiP2P header loaded
+ bool header_loaded;
+
+ /**
+ * Loads filter file
+ */
+ void load();
+
+ /**
+ * Binary searches AntiP2P::blocks to find range where IP could be.
+ * @returns
+ * -1 if IP cannot be in the list
+ * -2 if IP is already found in blocks
+ * or index of HeaderBlock in AntiP2P::blocks which will be used for direct file search.
+ **/
+ int searchHeader(bt::Uint32& ip, int start, int end);
+
+
+ /**
+ * Binary searches AntiP2P::file to find IP.
+ * @returns TRUE if IP should be blocked FALSE otherwise
+ **/
+ bool searchFile(IPBlock* file_blocks, bt::Uint32& ip, int start, int end);
+
+ };
+}
+#endif
diff --git a/plugins/ipfilter/convert_dlg.ui b/plugins/ipfilter/convert_dlg.ui
new file mode 100644
index 0000000..cc246ed
--- /dev/null
+++ b/plugins/ipfilter/convert_dlg.ui
@@ -0,0 +1,157 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ConvertingDlg</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>ConvertingDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>414</width>
+ <height>156</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Converting...</string>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="resizeMode">
+ <enum>Fixed</enum>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Click on the 'convert' button to start converting antip2p file. NOTE: This process could take a while even on fast machines and during that time you will not be able to use KTorrent.</string>
+ </property>
+ <property name="textFormat">
+ <enum>AutoText</enum>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ <property name="indent">
+ <number>-2</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lbl_progress</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KProgress" row="2" column="0">
+ <property name="name">
+ <cstring>kProgress1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>390</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>label1</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnClose</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Close</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnCancel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>C&amp;ancel</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>btnClose</sender>
+ <signal>clicked()</signal>
+ <receiver>ConvertingDlg</receiver>
+ <slot>btnClose_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>ConvertingDlg</receiver>
+ <slot>btnCancel_clicked()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>btnClose_clicked()</slot>
+ <slot>btnCancel_clicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kprogress.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/ipfilter/convertdialog.cpp b/plugins/ipfilter/convertdialog.cpp
new file mode 100644
index 0000000..6fed40f
--- /dev/null
+++ b/plugins/ipfilter/convertdialog.cpp
@@ -0,0 +1,262 @@
+/***************************************************************************
+* Copyright (C) 2005 by Joris Guisson *
+* joris.guisson@gmail.com *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License *
+* along with this program; if not, write to the *
+* Free Software Foundation, Inc., *
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+***************************************************************************/
+#include "convertdialog.h"
+
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kmessagebox.h>
+#include <kio/netaccess.h>
+#include <kprogress.h>
+#include <kmimetype.h>
+
+#include <util/log.h>
+#include <util/constants.h>
+#include <torrent/globals.h>
+#include <interfaces/coreinterface.h>
+
+#include <qfile.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+#include <qvalidator.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qevent.h>
+#include <qurloperator.h>
+#include "antip2p.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+
+ Uint32 toUint32(QString& ip)
+ {
+ bool test;
+ Uint32 ret = ip.section('.',0,0).toULongLong(&test);
+ ret <<= 8;
+ ret |= ip.section('.',1,1).toULong(&test);
+ ret <<= 8;
+ ret |= ip.section('.',2,2).toULong(&test);
+ ret <<= 8;
+ ret |= ip.section('.',3,3).toULong(&test);
+
+ return ret;
+ }
+
+ IPBlock RangeToBlock(const QString& range)
+ {
+ IPBlock block;
+ QStringList ls = QStringList::split('-', range);
+ block.ip1 = toUint32(ls[0]);
+ block.ip2 = toUint32(ls[1]);
+ return block;
+ }
+
+ ConvertDialog::ConvertDialog( IPFilterPlugin* p, QWidget *parent, const char *name )
+ : ConvertingDlg( parent, name )
+ {
+ m_plugin = p;
+ btnClose->setText(i18n("Convert"));
+ to_convert = true;
+ converting = false;
+ canceled = false;
+ kProgress1->setEnabled(false);
+ }
+
+ void ConvertDialog::convert()
+ {
+ QFile source( KGlobal::dirs() ->saveLocation( "data", "ktorrent" ) + "level1.txt" );
+ QFile target( KGlobal::dirs() ->saveLocation( "data", "ktorrent" ) + "level1.dat" );
+ QFile temp( KGlobal::dirs() ->saveLocation( "data", "ktorrent" ) + "level1.dat.tmp" );
+
+ if(target.exists())
+ {
+ //make backup
+ KIO::NetAccess::file_copy(KGlobal::dirs() ->saveLocation( "data", "ktorrent" ) + "level1.dat", KGlobal::dirs() ->saveLocation( "data", "ktorrent" ) + "level1.dat.tmp", -1, true);
+ }
+
+ /* READ INPUT FILE */
+ QValueList<IPBlock> list;
+ lbl_progress->setText( i18n( "Loading txt file..." ) );
+ label1->setText( i18n("Please wait...") );
+ ulong source_size = source.size();
+ btnClose->setEnabled( false );
+ converting = true;
+
+ int counter = 0;
+
+ if ( source.open( IO_ReadOnly ) )
+ {
+ QTextStream stream( &source );
+ kProgress1->setEnabled(true);
+
+ int i = 0;
+ QRegExp rx( "[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}-[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}" );
+ QRegExpValidator v( rx, 0 );
+ int poz = 0;
+
+ while ( !stream.atEnd() )
+ {
+ if(canceled)
+ return;
+
+ KApplication::kApplication() ->processEvents();
+ QString line = stream.readLine();
+ i += line.length() * sizeof( char ); //rough estimation of string size
+ kProgress1->setProgress( i * 100 / source_size );
+ ++i;
+
+ QString ip_part = line.section( ':' , -1 );
+ if ( v.validate( ip_part, poz ) != QValidator::Acceptable )
+ continue;
+ else
+ ++counter;
+
+ list += RangeToBlock(ip_part);
+ }
+ source.close();
+ }
+ else
+ {
+ Out(SYS_IPF|LOG_IMPORTANT) << "Cannot find level1 file" << endl;
+ btnClose->setEnabled( true );
+ btnClose->setText(i18n("&Close"));
+ label1->setText("");
+ to_convert = false;
+ converting = false;
+ return ;
+ }
+
+ if ( counter != 0 )
+ {
+ qHeapSort(list);
+ lbl_progress->setText( i18n( "Converting..." ) );
+ if ( m_plugin )
+ m_plugin->unloadAntiP2P();
+
+ ulong blocks = list.count();
+
+ /** WRITE TO OUTPUT **/
+ if ( !target.open( IO_WriteOnly ) )
+ {
+ Out(SYS_IPF|LOG_IMPORTANT) << "Unable to open file for writing" << endl;
+ btnClose->setEnabled( true );
+ btnClose->setText(i18n("&Close"));
+ label1->setText("");
+ to_convert = false;
+ converting = false;
+ return ;
+ }
+
+ Out(SYS_IPF|LOG_NOTICE) << "Loading finished. Starting conversion..." << endl;
+
+ QValueList<IPBlock>::iterator iter;
+ int i = 0;
+ for (iter = list.begin(); iter != list.end(); ++iter, ++i)
+ {
+ IPBlock & block = *iter;
+ target.writeBlock( ( char* ) & block, sizeof( IPBlock ) );
+ if ( i % 1000 == 0 )
+ {
+ kProgress1->setProgress( ( int ) 100 * i / blocks );
+ if ( i % 10000 == 0 )
+ Out(SYS_IPF|LOG_DEBUG) << "Block " << i << " written." << endl;
+ }
+ KApplication::kApplication()->processEvents();
+
+ if(canceled)
+ return;
+ }
+ kProgress1->setProgress(100);
+ Out(SYS_IPF|LOG_NOTICE) << "Finished converting." << endl;
+ lbl_progress->setText( i18n( "File converted." ) );
+ target.close();
+ }
+ else
+ {
+ lbl_progress->setText( "<font color=\"#ff0000\">" + i18n( "Could not load filter:" ) + "</font>" + i18n( "Bad filter file. It may be corrupted or has a bad format." ) );
+ target.remove();
+ source.remove();
+ btnClose->setEnabled( true );
+ btnClose->setText(i18n("&Close"));
+ label1->setText("");
+ to_convert = false;
+ converting = false;
+ }
+
+ KApplication::kApplication()->processEvents();
+ //reload level1 filter
+ if ( m_plugin )
+ m_plugin->loadAntiP2P();
+
+ btnClose->setEnabled( true );
+ to_convert = false;
+ converting = false;
+ btnClose->setText(i18n("&Close"));
+ label1->setText("");
+ }
+
+ void ConvertDialog::btnClose_clicked()
+ {
+ if(to_convert)
+ convert();
+ else
+ this->close();
+ }
+
+ void ConvertDialog::closeEvent(QCloseEvent* e)
+ {
+ if(!converting)
+ e->accept();
+ else
+ e->ignore();
+ }
+
+ void ConvertDialog::btnCancel_clicked()
+ {
+ if(converting)
+ {
+ QFile target( KGlobal::dirs() ->saveLocation( "data", "ktorrent" ) + "level1.dat" );
+ if(target.exists())
+ target.remove();
+
+ QFile temp( KGlobal::dirs() ->saveLocation( "data", "ktorrent" ) + "level1.dat.tmp");
+ if(temp.exists())
+ {
+ KIO::NetAccess::file_copy(KGlobal::dirs() ->saveLocation( "data", "ktorrent" ) + "level1.dat.tmp", KGlobal::dirs() ->saveLocation( "data", "ktorrent" ) + "level1.dat", -1, true);
+ temp.remove();
+ }
+
+ canceled = true;
+ Out(SYS_IPF|LOG_NOTICE) << "Conversion canceled." << endl;
+ }
+
+
+ this->reject();
+ }
+
+}
+
+#include "convertdialog.moc"
diff --git a/plugins/ipfilter/convertdialog.h b/plugins/ipfilter/convertdialog.h
new file mode 100644
index 0000000..be451f3
--- /dev/null
+++ b/plugins/ipfilter/convertdialog.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+* Copyright (C) 2005 by Joris Guisson *
+* joris.guisson@gmail.com *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License *
+* along with this program; if not, write to the *
+* Free Software Foundation, Inc., *
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+***************************************************************************/
+#ifndef CONVERTDIALOG_H
+#define CONVERTDIALOG_H
+
+#include "convert_dlg.h"
+#include "ipfilterplugin.h"
+
+#include <qevent.h>
+
+namespace kt
+{
+ class ConvertDialog: public ConvertingDlg
+ {
+ Q_OBJECT
+
+ public:
+ ConvertDialog( IPFilterPlugin* p, QWidget *parent = 0, const char *name = 0 );
+
+ public slots:
+ virtual void btnClose_clicked();
+
+ private:
+ void convert();
+ IPFilterPlugin* m_plugin;
+ bool to_convert;
+ bool converting;
+ bool canceled;
+
+ private slots:
+ void closeEvent(QCloseEvent* e);
+ virtual void btnCancel_clicked();
+ };
+}
+#endif
diff --git a/plugins/ipfilter/ipblockingpref.ui b/plugins/ipfilter/ipblockingpref.ui
new file mode 100644
index 0000000..43870f0
--- /dev/null
+++ b/plugins/ipfilter/ipblockingpref.ui
@@ -0,0 +1,204 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>IPBlockingPref</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>IPBlockingPref</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>564</width>
+ <height>444</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>IPBlocking Preferences</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Select PeerGuardian Filter File </string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>checkUseLevel1</cstring>
+ </property>
+ <property name="text">
+ <string>Use PeerGuardian filter?</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>IP filter file:</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>m_url</cstring>
+ </property>
+ <property name="url" stdset="0">
+ <string>http://www.bluetack.co.uk/modules.php?name=Downloads&amp;d_op=getit&amp;lid=8</string>
+ </property>
+ <property name="showLocalProtocol">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>361</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnDownload</cstring>
+ </property>
+ <property name="text">
+ <string>Dow&amp;nload/Convert</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Download PeerGuardian filter from bluetack.co.uk or blocklist.org.
+NOTE: ZIP file from bluetack.co.uk is supported.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>lbl_status1</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer row="4" column="0">
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>btnDownload</sender>
+ <signal>clicked()</signal>
+ <receiver>IPBlockingPref</receiver>
+ <slot>btnDownload_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>checkUseLevel1</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>IPBlockingPref</receiver>
+ <slot>checkUseLevel1_toggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>checkUseLevel1</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>checkUseLevel1</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_3</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>btnDownload_clicked()</slot>
+ <slot>checkUseLevel1_toggled(bool)</slot>
+ <slot>checkUseKTfilter_toggled(bool)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/ipfilter/ipblockingprefpage.cpp b/plugins/ipfilter/ipblockingprefpage.cpp
new file mode 100644
index 0000000..6d06d1b
--- /dev/null
+++ b/plugins/ipfilter/ipblockingprefpage.cpp
@@ -0,0 +1,258 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "ipblockingprefpage.h"
+#include "ipblockingpref.h"
+#include "ipfilterpluginsettings.h"
+#include "ipfilterplugin.h"
+#include "convertdialog.h"
+
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kurlrequester.h>
+#include <kurl.h>
+#include <kmessagebox.h>
+#include <kio/netaccess.h>
+#include <kprogress.h>
+#include <kmimetype.h>
+
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <interfaces/coreinterface.h>
+
+#include <qthread.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qregexp.h>
+#include <qvalidator.h>
+#include <qlayout.h>
+#include <qdialog.h>
+#include <qobject.h>
+
+using namespace bt;
+
+#define MAX_RANGES 500
+
+namespace kt
+{
+ IPBlockingPrefPageWidget::IPBlockingPrefPageWidget(QWidget* parent) : IPBlockingPref(parent)
+ {
+ m_url->setURL(IPBlockingPluginSettings::filterURL());
+ if (m_url->url() == "")
+ m_url->setURL(QString("http://www.bluetack.co.uk/config/splist.zip"));
+
+ bool use_level1 = IPBlockingPluginSettings::useLevel1();
+
+ checkUseLevel1->setChecked(use_level1);
+
+ if(use_level1)
+ {
+ lbl_status1->setText(i18n("Status: Loaded and running."));
+ m_url->setEnabled(true);
+ btnDownload->setEnabled(true);
+ }
+ else
+ {
+ lbl_status1->setText(i18n("Status: Not loaded."));
+ m_url->setEnabled(false);
+ btnDownload->setEnabled(false);
+ }
+
+ m_plugin = 0;
+ }
+
+ void IPBlockingPrefPageWidget::apply()
+ {
+ IPBlockingPluginSettings::setFilterURL(m_url->url());
+ IPBlockingPluginSettings::setUseLevel1(checkUseLevel1->isChecked());
+ IPBlockingPluginSettings::writeConfig();
+
+ if(checkUseLevel1->isChecked())
+ {
+ QFile target(KGlobal::dirs()->saveLocation("data","ktorrent") + "level1.dat");
+ if(target.exists())
+ lbl_status1->setText(i18n("Status: Loaded and running."));
+ else
+ lbl_status1->setText(i18n("Status: <font color=\"#ff0000\">Filter file not found.</font> Download and convert filter file."));
+ }
+ else
+ lbl_status1->setText(i18n("Status: Not loaded."));
+ }
+
+ void IPBlockingPrefPageWidget::btnDownload_clicked()
+ {
+ QString target(KGlobal::dirs()->saveLocation("data","ktorrent") + "level1");
+ QFile target_file(target);
+ QFile txtfile(target + ".txt");
+ KURL url(m_url->url());
+ KURL dest(target);
+ KURL temp(KGlobal::dirs()->saveLocation("data","ktorrent") + "level1.tmp");
+ if(KIO::NetAccess::exists(temp,false, this))
+ KIO::NetAccess::del(temp,this);
+
+ bool download = true;
+
+ if(txtfile.exists())
+ {
+ if((KMessageBox::questionYesNo(this, i18n("Selected file already exists, do you want to download it again?"),i18n("File Exists")) == 4))
+ download = false;
+ else
+ KIO::NetAccess::move(target, temp);
+ }
+
+ if(download)
+ {
+ if(!url.isLocalFile())
+ {
+ if (KIO::NetAccess::download(url,target,NULL))
+ {
+ //Level1 list successfully downloaded, remove temporary file
+ KIO::NetAccess::removeTempFile(target);
+ KIO::NetAccess::del(temp, this);
+ }
+ else
+ {
+ QString err = KIO::NetAccess::lastErrorString();
+ if(err != QString::null)
+ KMessageBox::error(0,KIO::NetAccess::lastErrorString(),i18n("Error"));
+ else
+ KIO::NetAccess::move(temp, target);
+
+
+ //we don't want to convert since download failed
+ return;
+ }
+ }
+ else
+ {
+ if (!KIO::NetAccess::file_copy(url,dest, -1, true))
+ {
+ KMessageBox::error(0,KIO::NetAccess::lastErrorString(),i18n("Error"));
+ return;
+ }
+ }
+
+ //now determine if it's ZIP or TXT file
+ KMimeType::Ptr ptr = KMimeType::findByPath(target);
+ if(ptr->name() == "application/x-zip")
+ {
+ KURL zipfile("zip:" + target + "/splist.txt");
+ KURL destinationfile(target + ".txt");
+ KIO::NetAccess::file_copy(zipfile,destinationfile, -1, true);
+ }
+ else
+ {
+ KURL zipfile(target);
+ KURL destinationfile(target + ".txt");
+ KIO::NetAccess::file_copy(zipfile,destinationfile, -1, true);
+ }
+
+ }
+ convert();
+ }
+
+ void IPBlockingPrefPageWidget::checkUseLevel1_toggled(bool check)
+ {
+ if(check)
+ {
+ m_url->setEnabled(true);
+ btnDownload->setEnabled(true);
+ }
+ else
+ {
+ lbl_status1->setText("");
+ m_url->setEnabled(false);
+ btnDownload->setEnabled(false);
+ }
+ }
+
+ void IPBlockingPrefPageWidget::convert()
+ {
+ QFile target(KGlobal::dirs()->saveLocation("data","ktorrent") + "level1.dat");
+ if(target.exists())
+ {
+ if((KMessageBox::questionYesNo(this,i18n("Filter file (level1.dat) already exists, do you want to convert it again?"),i18n("File Exists")) == 4))
+ return;
+// else
+// KIO::NetAccess::del(KGlobal::dirs()->saveLocation("data","ktorrent") + "level1.dat", NULL);
+ }
+ ConvertDialog dlg(m_plugin);
+ dlg.exec();
+ }
+
+ void IPBlockingPrefPageWidget::setPlugin(IPFilterPlugin* p)
+ {
+ m_plugin = p;
+ }
+
+ void IPBlockingPrefPageWidget::setPrefPage( IPBlockingPrefPage * p )
+ {
+ m_prefpage = p;
+ }
+
+ void IPBlockingPrefPageWidget::setConverting(bool enable)
+ {
+ btnDownload->setEnabled(enable);
+ lbl_status1->setText("");
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////////
+
+ IPBlockingPrefPage::IPBlockingPrefPage(CoreInterface* core, IPFilterPlugin* p)
+ : PrefPageInterface(i18n("IPBlocking Filter"), i18n("IPBlocking Filter Options"), KGlobal::iconLoader()->loadIcon("filter",KIcon::NoGroup)), m_core(core), m_plugin(p)
+ {
+ widget = 0;
+ }
+
+ IPBlockingPrefPage::~IPBlockingPrefPage()
+ {}
+
+ bool IPBlockingPrefPage::apply()
+ {
+ widget->apply();
+
+ if(IPBlockingPluginSettings::useLevel1())
+ m_plugin->loadAntiP2P();
+ else
+ m_plugin->unloadAntiP2P();
+
+ return true;
+ }
+
+ void IPBlockingPrefPage::createWidget(QWidget* parent)
+ {
+ widget = new IPBlockingPrefPageWidget(parent);
+ widget->setPlugin(m_plugin);
+ widget->setPrefPage(this);
+ }
+
+ void IPBlockingPrefPage::deleteWidget()
+ {
+ delete widget;
+ widget = 0;
+ }
+
+ void IPBlockingPrefPage::updateData()
+ {}
+}
diff --git a/plugins/ipfilter/ipblockingprefpage.h b/plugins/ipfilter/ipblockingprefpage.h
new file mode 100644
index 0000000..3779965
--- /dev/null
+++ b/plugins/ipfilter/ipblockingprefpage.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef IPBLOCKINGPREFPAGE_H
+#define IPBLOCKINGPREFPAGE_H
+
+#include <interfaces/prefpageinterface.h>
+#include "ipblockingpref.h"
+#include "ipfilterplugin.h"
+#include <interfaces/coreinterface.h>
+#include <qthread.h>
+#include <qobject.h>
+
+class KProgress;
+
+namespace kt
+{
+ class IPFilterPlugin;
+ class IPBlockingPrefPage;
+
+ /**
+ @author Ivan Vasic
+ */
+ class IPBlockingPrefPageWidget : public IPBlockingPref
+ {
+ public:
+ IPBlockingPrefPageWidget(QWidget *parent = 0);
+ void apply();
+ void convert();
+ void setPlugin(IPFilterPlugin* p);
+ void setPrefPage(IPBlockingPrefPage* p);
+
+ //used with ConvertThread to enable/disable controls while converting
+ void setConverting(bool enable);
+
+ public slots:
+ virtual void btnDownload_clicked();
+ virtual void checkUseLevel1_toggled(bool);
+
+ private:
+ IPFilterPlugin* m_plugin;
+ IPBlockingPrefPage* m_prefpage;
+ };
+
+ /**
+ * @author Ivan Vasic
+ * @brief IPBlocking plugin interface page
+ **/
+ class IPBlockingPrefPage : public PrefPageInterface
+ {
+ public:
+ IPBlockingPrefPage(CoreInterface* core, IPFilterPlugin* p);
+ virtual ~IPBlockingPrefPage();
+
+ virtual bool apply();
+ virtual void createWidget(QWidget* parent);
+ virtual void updateData();
+ virtual void deleteWidget();
+
+ private:
+ CoreInterface* m_core;
+ IPBlockingPrefPageWidget* widget;
+ IPFilterPlugin* m_plugin;
+ };
+}
+#endif
diff --git a/plugins/ipfilter/ipfilterplugin.cpp b/plugins/ipfilter/ipfilterplugin.cpp
new file mode 100644
index 0000000..2f53197
--- /dev/null
+++ b/plugins/ipfilter/ipfilterplugin.cpp
@@ -0,0 +1,129 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+
+#include <interfaces/coreinterface.h>
+#include <interfaces/guiinterface.h>
+#include <util/constants.h>
+#include <torrent/ipblocklist.h>
+
+#include <qstring.h>
+
+#include "ipfilterplugin.h"
+#include "ipfilterpluginsettings.h"
+#include "antip2p.h"
+
+using namespace bt;
+
+K_EXPORT_COMPONENT_FACTORY(ktipfilterplugin,KGenericFactory<kt::IPFilterPlugin>("ipfilterplugin"))
+
+namespace kt
+{
+ const QString NAME = "IP Filter";
+ const QString AUTHOR = "Ivan Vasic";
+ const QString EMAIL = "ivasic@gmail.com";
+ const QString DESCRIPTION = i18n("Filters out unwanted peers based on their IP address");
+
+ IPFilterPlugin::IPFilterPlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name, args,NAME,i18n("IP Filter"),AUTHOR,EMAIL,DESCRIPTION,"filter")
+ {
+ // setXMLFile("ktpluginui.rc");
+ level1 = 0;
+ }
+
+
+ IPFilterPlugin::~IPFilterPlugin()
+ {
+ //...just in case something goes wrong...
+ IPBlocklist& ipblist = IPBlocklist::instance();
+ ipblist.unsetPluginInterfacePtr();
+ }
+
+ void IPFilterPlugin::load()
+ {
+ pref = new IPBlockingPrefPage(getCore(), this);
+ getGUI()->addPrefPage(pref);
+
+ if(IPBlockingPluginSettings::useLevel1())
+ loadAntiP2P();
+
+ //now we need to set a pointer to the IPBlocklist
+ IPBlocklist& ipblist = IPBlocklist::instance();
+ ipblist.setPluginInterfacePtr(this);
+ }
+
+ void IPFilterPlugin::unload()
+ {
+ //First unset pointer in IPBlocklist
+ IPBlocklist& ipblist = IPBlocklist::instance();
+ ipblist.unsetPluginInterfacePtr();
+
+ getGUI()->removePrefPage(pref);
+ delete pref;
+ pref = 0;
+ if(level1)
+ {
+ delete level1;
+ level1 = 0;
+ }
+ }
+
+ bool IPFilterPlugin::loadAntiP2P()
+ {
+ if(level1 != 0)
+ return true;
+ level1 = new AntiP2P();
+ if(!level1->exists())
+ {
+ delete level1;
+ level1 = 0;
+ return false;
+ }
+ level1->loadHeader();
+ return true;
+ }
+
+ bool IPFilterPlugin::unloadAntiP2P()
+ {
+ if(level1 != 0)
+ {
+ delete level1;
+ level1 = 0;
+ return true;
+ }
+ else
+ //anything else to check?
+ return true;
+ }
+
+ bool IPFilterPlugin::isBlockedIP(const QString& ip)
+ {
+ if (level1 == 0)
+ return false;
+
+ return level1->isBlockedIP(ip);
+ }
+
+ bool IPFilterPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+}
diff --git a/plugins/ipfilter/ipfilterplugin.h b/plugins/ipfilter/ipfilterplugin.h
new file mode 100644
index 0000000..2e9c984
--- /dev/null
+++ b/plugins/ipfilter/ipfilterplugin.h
@@ -0,0 +1,73 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTIPFILTERPLUGIN_H
+#define KTIPFILTERPLUGIN_H
+
+#include <interfaces/plugin.h>
+#include <interfaces/ipblockinginterface.h>
+
+#include "ipblockingprefpage.h"
+#include "antip2p.h"
+
+class QString;
+
+namespace kt
+{
+ class IPBlockingPrefPage;
+
+ /**
+ * @author Ivan Vasic <ivasic@gmail.com>
+ * @brief IP filter plugin
+ *
+ * This plugin will load IP ranges from specific files into KT IPBlocklist.
+ */
+ class IPFilterPlugin : public Plugin, public kt::IPBlockingInterface
+ {
+ Q_OBJECT
+ public:
+ IPFilterPlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~IPFilterPlugin();
+
+ virtual void load();
+ virtual void unload();
+
+ ///Loads the KT format list filter
+ void loadFilters();
+
+ ///Loads the anti-p2p filter list
+ bool loadAntiP2P();
+
+ ///Unloads the anti-p2p filter list
+ bool unloadAntiP2P();
+
+
+ ///Checks if IP is listed in AntiP2P filter list.
+ bool isBlockedIP(const QString& ip);
+
+ bool versionCheck(const QString & version) const;
+ private:
+ IPBlockingPrefPage* pref;
+ AntiP2P* level1;
+ };
+
+}
+
+#endif
diff --git a/plugins/ipfilter/ipfilterpluginsettings.kcfgc b/plugins/ipfilter/ipfilterpluginsettings.kcfgc
new file mode 100644
index 0000000..b9aeaeb
--- /dev/null
+++ b/plugins/ipfilter/ipfilterpluginsettings.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=ktipfilterplugin.kcfg
+ClassName=IPBlockingPluginSettings
+Namespace=kt
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables \ No newline at end of file
diff --git a/plugins/ipfilter/ktipfilterplugin.desktop b/plugins/ipfilter/ktipfilterplugin.desktop
new file mode 100644
index 0000000..3c442a9
--- /dev/null
+++ b/plugins/ipfilter/ktipfilterplugin.desktop
@@ -0,0 +1,56 @@
+[Desktop Entry]
+Name=IPFilterPlugin
+Name[bg]=Приставка за IP-филтриране
+Name[br]=Lugent Sil IP
+Name[de]=IP-Filter-Modul
+Name[el]=Πρόσθετο φίλτρου IP
+Name[es]=Filtro de IP
+Name[et]=IP-filtri plugin
+Name[it]=Plugin filtro ip
+Name[nb]=IP-filtermodul
+Name[nds]="IP-Filter"-Moduul
+Name[nl]=IPFilter-plugin
+Name[pl]=Wtyczka filtru IP
+Name[pt]=Filtro de IPs
+Name[pt_BR]=Plugin de Filtro de IP
+Name[sk]=IPFilter Plugin
+Name[sr]=Прикључак IP филтера
+Name[sr@Latn]=Priključak IP filtera
+Name[sv]=IP-filterinsticksprogram
+Name[tr]=IP Filtre Eklentisi
+Name[xx]=xxIPFilterPluginxx
+Name[zh_CN]=IP 过滤器插件
+Name[zh_TW]=IPFilter外掛程式
+Comment=IP filter plugin for KTorrent
+Comment[ar]=قابس مرشح IP لِــ KTorrent
+Comment[bg]=Приставка за IP-филтриране (KTorrent)
+Comment[br]=Lugent sil IP evit KTorrentt
+Comment[ca]=Connector de filtres d'IP per a Ktorrent
+Comment[cs]=IP filtr modul pro KTorrent
+Comment[de]=IP-Filter-Modul für KTorrent
+Comment[el]=Πρόσθετο φίλτρου IP για το KTorrent
+Comment[es]=Complemento de filtro de IP de KTorrent
+Comment[et]=KTorrenti IP-filtri plugin
+Comment[fa]=وصلۀ پالایۀ IP برای KTorrent
+Comment[gl]=Plugin de filtrado de IPs para KTorrent
+Comment[it]=Plugin filtro ip per KTorrent
+Comment[ja]=KTorrent のための IP フィルタプラグイン
+Comment[ka]=KTorrent-ის IP ფილტრაციის მოდული
+Comment[nb]=IP-filtermodul for KTorrent
+Comment[nds]="IP-Filter"-Moduul för KTorrent
+Comment[nl]=Plugin om op IP-adressen te filteren in KTorrent
+Comment[pl]=Wtyczka filtru IP dla KTorrent
+Comment[pt]='Plugin' de filtragem de IPs do KTorrent
+Comment[pt_BR]=Plugin de Filtro de IP para o KTorrent
+Comment[sk]=IP filter plugin pre KTorrent
+Comment[sr]=Прикључак IP филтера за KTorrent
+Comment[sr@Latn]=Priključak IP filtera za KTorrent
+Comment[sv]=IP-filterinsticksprogram för Ktorrent
+Comment[tr]=KTorrent için IP filtre eklentisi
+Comment[uk]=Втулок фільтрування IP для KTorrent
+Comment[xx]=xxIP filter plugin for KTorrentxx
+Comment[zh_CN]=KTorrent 的 IP 过滤器插件
+Comment[zh_TW]=KTorrent IP 過濾器外掛程式
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktipfilterplugin
diff --git a/plugins/ipfilter/ktipfilterplugin.kcfg b/plugins/ipfilter/ktipfilterplugin.kcfg
new file mode 100644
index 0000000..18ab419
--- /dev/null
+++ b/plugins/ipfilter/ktipfilterplugin.kcfg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <kcfgfile name="ktipfilterpluginrc"/>
+ <group name="general">
+ <entry name="filterURL" type="String">
+ <label>Level1 filter url</label>
+ <default></default>
+ </entry>
+ <entry name="useLevel1" type="Bool">
+ <label>Use level1 filter?</label>
+ <default>FALSE</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/logviewer/Makefile.am b/plugins/logviewer/Makefile.am
new file mode 100644
index 0000000..c925a12
--- /dev/null
+++ b/plugins/logviewer/Makefile.am
@@ -0,0 +1,33 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+
+METASOURCES = AUTO
+kde_module_LTLIBRARIES = ktlogviewerplugin.la
+
+
+# Libs needed by the plugin
+ktlogviewerplugin_la_LIBADD = $(LIB_KHTML) $(LIB_KPARTS) \
+ ../../libktorrent/libktorrent.la $(LIB_QT) \
+ $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KFILE)
+
+
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktlogviewerplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+# rc file containing the GUI for the plugin
+# pluginsdir = $(kde_datadir)/ktsearchplugin
+# plugins_DATA = ktsearchpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+kde_services_DATA = ktlogviewerplugin.desktop
+
+kde_kcfg_DATA = ktlogviewerplugin.kcfg
+
+noinst_HEADERS = logviewerplugin.h logviewer.h logprefpage.h logprefwidget.h \
+ logflags.h
+ktlogviewerplugin_la_SOURCES = logviewerplugin.cpp logviewer.cpp \
+ logprefpage.cpp logprefwidgetbase.ui logprefwidget.cpp logviewerpluginsettings.kcfgc \
+ logflags.cpp
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/logviewer/ktlogviewerplugin.desktop b/plugins/logviewer/ktlogviewerplugin.desktop
new file mode 100644
index 0000000..ba2e625
--- /dev/null
+++ b/plugins/logviewer/ktlogviewerplugin.desktop
@@ -0,0 +1,26 @@
+[Desktop Entry]
+Name=LogViewerPlugin
+Name[bg]=Приставка за преглед на журнални файлове
+Name[de]=Modul zur Anzeige von Protokolldateien
+Name[el]=Πρόσθετο προβολής καταγραφής
+Name[es]=Visualizador del registro
+Name[et]=Loginäitaja plugin
+Name[it]=Plugin visore registro
+Name[nb]=Logvisningsmodul
+Name[nds]=Logbookkieker-Moduul
+Name[nl]=Logweergaveplugin
+Name[pl]=Wtyczka wyświetlania dziennika
+Name[pt]=Visualizador do Registo
+Name[pt_BR]=Plugin de Vizualização de Log
+Name[sk]=LogViewer Plugin
+Name[sr]=Прикључак приказа дневника
+Name[sr@Latn]=Priključak prikaza dnevnika
+Name[sv]=Loggvisningsinsticksprogram
+Name[tr]=Kayıt Görüntü Eklentisi
+Name[uk]=Втулок перегляду журналу
+Name[xx]=xxLogViewerPluginxx
+Name[zh_CN]=日志查看器插件
+Name[zh_TW]=紀錄檢視器外掛程式
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktlogviewerplugin
diff --git a/plugins/logviewer/ktlogviewerplugin.kcfg b/plugins/logviewer/ktlogviewerplugin.kcfg
new file mode 100644
index 0000000..c6e4a25
--- /dev/null
+++ b/plugins/logviewer/ktlogviewerplugin.kcfg
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <group name="general">
+
+ <entry name = "useRichText" type ="Bool">
+ <label>Use rich log text</label>
+ <default>false</default>
+ </entry>
+
+ <entry name="sysGEN" type="UInt">
+ <label>General info messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysCON" type="UInt">
+ <label>Connections messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysDHT" type="UInt">
+ <label>DHT messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysTRK" type="UInt">
+ <label>Tracker info messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysDIO" type="UInt">
+ <label>Disk IO info messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysIPF" type="UInt">
+ <label>IPFilter plugin messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysSRC" type="UInt">
+ <label>Search plugin messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysPFI" type="UInt">
+ <label>PartFileImport plugin messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysINW" type="UInt">
+ <label>InfoWidget plugin messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysPNP" type="UInt">
+ <label>UPnP plugin messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysSNF" type="UInt">
+ <label>ScanFolder plugin messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysSCD" type="UInt">
+ <label>Scheduler plugin messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysRSS" type="UInt">
+ <label>RSS plugin messages flag</label>
+ <default>0</default>
+ </entry>
+ <entry name="sysWEB" type="UInt">
+ <label>WebInterface plugin messages flag</label>
+ <default>0</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/logviewer/logflags.cpp b/plugins/logviewer/logflags.cpp
new file mode 100644
index 0000000..ef1ba69
--- /dev/null
+++ b/plugins/logviewer/logflags.cpp
@@ -0,0 +1,170 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "logflags.h"
+#include "logviewer.h"
+#include "logviewerpluginsettings.h"
+
+#include <util/log.h>
+#include <torrent/globals.h>
+
+#include <qstring.h>
+
+using namespace bt;
+
+namespace kt
+{
+
+ LogFlags* LogFlags::self = 0;
+ LogViewer* LogFlags::m_log = 0;
+
+ LogFlags::LogFlags()
+ {
+ updateFlags();
+ }
+
+ LogFlags::~LogFlags()
+ {}
+
+ LogFlags& LogFlags::instance()
+ {
+ if (!self)
+ self = new LogFlags();
+ return *self;
+ }
+
+ bool LogFlags::checkFlags(unsigned int arg)
+ {
+ if(arg & SYS_GEN)
+ return m_flags.SYSGEN & arg;
+
+ if(arg & SYS_CON)
+ return (arg & m_flags.SYSCON) && ((arg & 0x0000000F) <= m_flags.SYSCON);
+
+ if(arg & SYS_DHT)
+ return (arg & m_flags.SYSDHT) && ((arg & 0x0000000F) <= m_flags.SYSDHT);
+
+ if(arg & SYS_TRK)
+ return (arg & m_flags.SYSTRK) && ((arg & 0x0000000F) <= m_flags.SYSTRK);
+
+ if(arg & SYS_DIO)
+ return (arg & m_flags.SYSDIO) && ((arg & 0x0000000F) <= m_flags.SYSDIO);
+
+ if(arg & SYS_INW)
+ return (arg & m_flags.SYSINW) && ((arg & 0x0000000F) <= m_flags.SYSINW);
+
+ if(arg & SYS_IPF)
+ return (arg & m_flags.SYSIPF) && ((arg & 0x0000000F) <= m_flags.SYSIPF);
+
+ if(arg & SYS_PFI)
+ return (arg & m_flags.SYSPFI) && ((arg & 0x0000000F) <= m_flags.SYSPFI);
+
+ if(arg & SYS_PNP)
+ return (arg & m_flags.SYSPNP) && ((arg & 0x0000000F) <= m_flags.SYSPNP);
+
+ if(arg & SYS_SCD)
+ return (arg & m_flags.SYSSCD) && ((arg & 0x0000000F) <= m_flags.SYSSCD);
+
+ if(arg & SYS_SNF)
+ return (arg & m_flags.SYSSNF) && ((arg & 0x0000000F) <= m_flags.SYSSNF);
+
+ if(arg & SYS_SRC)
+ return (arg & m_flags.SYSSRC) && ((arg & 0x0000000F) <= m_flags.SYSSRC);
+
+ if(arg & SYS_RSS)
+ return (arg & m_flags.SYSRSS) && ((arg & 0x0000000F) <= m_flags.SYSRSS);
+
+ if(arg & SYS_WEB)
+ return (arg & m_flags.SYSWEB) && ((arg & 0x0000000F) <= m_flags.SYSWEB);
+
+ return true;
+ }
+
+ void LogFlags::updateFlags()
+ {
+ m_flags.SYSGEN = LogViewerPluginSettings::sysGEN();
+ m_flags.SYSCON = LogViewerPluginSettings::sysCON();
+ m_flags.SYSDHT = LogViewerPluginSettings::sysDHT();
+ m_flags.SYSTRK = LogViewerPluginSettings::sysTRK();
+ m_flags.SYSDIO = LogViewerPluginSettings::sysDIO();
+
+ m_flags.SYSINW = LogViewerPluginSettings::sysINW();
+ m_flags.SYSIPF = LogViewerPluginSettings::sysIPF();
+ m_flags.SYSPFI = LogViewerPluginSettings::sysPFI();
+ m_flags.SYSPNP = LogViewerPluginSettings::sysPNP();
+ m_flags.SYSSCD = LogViewerPluginSettings::sysSCD();
+ m_flags.SYSSNF = LogViewerPluginSettings::sysSNF();
+ m_flags.SYSSRC = LogViewerPluginSettings::sysSRC();
+ m_flags.SYSRSS = LogViewerPluginSettings::sysRSS();
+ m_flags.SYSWEB = LogViewerPluginSettings::sysWEB();
+
+ m_useRichText = LogViewerPluginSettings::useRichText();
+
+ if(m_log)
+ m_log->setRichText(m_useRichText);
+ }
+
+ void LogFlags::finalize()
+ {
+ delete self;
+ self = 0;
+ m_log = 0;
+ }
+
+ bool LogFlags::useRichText()
+ {
+ return m_useRichText;
+ }
+
+ void LogFlags::setLog(LogViewer* log)
+ {
+ m_log = log;
+ }
+
+ QString& LogFlags::getFormattedMessage(unsigned int arg, QString& line)
+ {
+ if( (arg & LOG_ALL) == LOG_ALL)
+ {
+ return line;
+ }
+
+ if(arg & 0x04)
+ {
+ line.prepend("<font color=\"#646464\">");
+ line.append("</font>");
+ return line;
+ }
+
+ if(arg & 0x02)
+ {
+ line.prepend("<font color=\"#000000\">");
+ line.append("</font>");
+ return line;
+ }
+
+ if(arg & 0x01)
+ {
+ line.prepend("<font color=\"#460000\">");
+ line.append("</font>");
+ return line;
+ }
+
+ return line;
+ }
+}
diff --git a/plugins/logviewer/logflags.h b/plugins/logviewer/logflags.h
new file mode 100644
index 0000000..9d7245a
--- /dev/null
+++ b/plugins/logviewer/logflags.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTLOGFLAGS_H
+#define KTLOGFLAGS_H
+
+class QString;
+
+namespace kt
+{
+ struct _logFlags
+ {
+ unsigned int SYSCON;
+ unsigned int SYSTRK;
+ unsigned int SYSDHT;
+ unsigned int SYSGEN;
+ unsigned int SYSDIO;
+
+ unsigned int SYSIPF;
+ unsigned int SYSSRC;
+ unsigned int SYSPNP;
+ unsigned int SYSINW;
+ unsigned int SYSSNF;
+ unsigned int SYSPFI;
+ unsigned int SYSSCD;
+ unsigned int SYSRSS;
+ unsigned int SYSWEB;
+ };
+
+ class LogViewer;
+
+ /**
+ * Class to read/save logging messages flags.
+ * @author Ivan Vasic <ivasic@gmail.com>
+ */
+ class LogFlags
+ {
+ public:
+ virtual ~LogFlags();
+
+ static LogFlags& instance();
+
+ ///Checks current flags with arg. Return true if message should be shown
+ bool checkFlags(unsigned int arg);
+
+ ///Updates flags from Settings::
+ void updateFlags();
+
+ ///Destroys this object
+ static void finalize();
+
+ ///Checks if LogViewer should print rich text format.
+ bool useRichText();
+
+ ///Sets a pointer to LogViewer
+ void setLog(LogViewer* log);
+
+ ///Makes line rich text according to arg level.
+ QString& getFormattedMessage(unsigned int arg, QString& line);
+
+ private:
+ LogFlags();
+
+ struct _logFlags m_flags;
+
+ static LogFlags* self;
+
+ static LogViewer* m_log;
+
+ bool m_useRichText;
+ };
+
+}
+
+#endif
diff --git a/plugins/logviewer/logprefpage.cpp b/plugins/logviewer/logprefpage.cpp
new file mode 100644
index 0000000..cc89a68
--- /dev/null
+++ b/plugins/logviewer/logprefpage.cpp
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasic *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "logprefpage.h"
+#include "logprefwidget.h"
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+
+namespace kt
+{
+
+ LogPrefPage::LogPrefPage()
+ : PrefPageInterface(i18n("LogViewer"), i18n("LogViewer Options"),
+ KGlobal::iconLoader()->loadIcon("toggle_log",KIcon::NoGroup))
+ {
+ m_widget = 0;
+ }
+
+
+ LogPrefPage::~LogPrefPage()
+ {}
+
+ bool LogPrefPage::apply()
+ {
+ if(m_widget)
+ return m_widget->apply();
+
+ return true;
+ }
+
+ void LogPrefPage::createWidget(QWidget* parent)
+ {
+ m_widget = new LogPrefWidget(parent);
+ }
+
+ void LogPrefPage::updateData()
+ {
+ }
+
+ void LogPrefPage::deleteWidget()
+ {
+ if(m_widget)
+ delete m_widget;
+ }
+}
diff --git a/plugins/logviewer/logprefpage.h b/plugins/logviewer/logprefpage.h
new file mode 100644
index 0000000..3aed61b
--- /dev/null
+++ b/plugins/logviewer/logprefpage.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasic *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTLOGPREFPAGE_H
+#define KTLOGPREFPAGE_H
+
+#include <interfaces/prefpageinterface.h>
+
+#include "logprefwidget.h"
+
+namespace kt
+{
+
+ /**
+ * LogViewer plugin preferences page
+ * @author Ivan Vasic <ivasic@gmail.com>
+ */
+ class LogPrefPage : public PrefPageInterface
+ {
+ public:
+ LogPrefPage();
+ virtual ~LogPrefPage();
+
+ virtual bool apply();
+ virtual void createWidget(QWidget* parent);
+ virtual void updateData();
+ virtual void deleteWidget();
+
+ private:
+ LogPrefWidget* m_widget;
+ };
+
+}
+
+#endif
diff --git a/plugins/logviewer/logprefwidget.cpp b/plugins/logviewer/logprefwidget.cpp
new file mode 100644
index 0000000..6bf99bd
--- /dev/null
+++ b/plugins/logviewer/logprefwidget.cpp
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasic *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "logprefwidget.h"
+#include "logviewerpluginsettings.h"
+#include "logflags.h"
+
+#include <kglobal.h>
+#include <klocale.h>
+
+#include <qwidget.h>
+#include <qstring.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+
+namespace kt
+{
+ LogPrefWidget::LogPrefWidget(QWidget *parent, const char *name)
+ :LogPrefWidgetBase(parent, name)
+ {
+ m_sysgen->setCurrentItem(getLevel(LogViewerPluginSettings::sysGEN()));
+ m_syscon->setCurrentItem(getLevel(LogViewerPluginSettings::sysCON()));
+ m_sysdht->setCurrentItem(getLevel(LogViewerPluginSettings::sysDHT()));
+ m_systrk->setCurrentItem(getLevel(LogViewerPluginSettings::sysTRK()));
+ m_sysdio->setCurrentItem(getLevel(LogViewerPluginSettings::sysDIO()));
+
+ m_sysipf->setCurrentItem(getLevel(LogViewerPluginSettings::sysIPF()));
+ m_syspfi->setCurrentItem(getLevel(LogViewerPluginSettings::sysPFI()));
+ m_sysinw->setCurrentItem(getLevel(LogViewerPluginSettings::sysINW()));
+ m_syspnp->setCurrentItem(getLevel(LogViewerPluginSettings::sysPNP()));
+ m_syssrc->setCurrentItem(getLevel(LogViewerPluginSettings::sysSRC()));
+ m_sysscd->setCurrentItem(getLevel(LogViewerPluginSettings::sysSCD()));
+ m_syssnf->setCurrentItem(getLevel(LogViewerPluginSettings::sysSNF()));
+ m_sysrss->setCurrentItem(getLevel(LogViewerPluginSettings::sysRSS()));
+ m_sysweb->setCurrentItem(getLevel(LogViewerPluginSettings::sysWEB()));
+
+ m_useRich->setChecked(LogViewerPluginSettings::useRichText());
+ }
+
+ bool LogPrefWidget::apply()
+ {
+ LogViewerPluginSettings::setSysGEN(getArg(m_sysgen->currentItem()));
+ LogViewerPluginSettings::setSysCON(getArg(m_syscon->currentItem()));
+ LogViewerPluginSettings::setSysDHT(getArg(m_sysdht->currentItem()));
+ LogViewerPluginSettings::setSysTRK(getArg(m_systrk->currentItem()));
+ LogViewerPluginSettings::setSysDIO(getArg(m_sysdio->currentItem()));
+
+ LogViewerPluginSettings::setSysIPF(getArg(m_sysipf->currentItem()));
+ LogViewerPluginSettings::setSysPFI(getArg(m_syspfi->currentItem()));
+ LogViewerPluginSettings::setSysINW(getArg(m_sysinw->currentItem()));
+ LogViewerPluginSettings::setSysPNP(getArg(m_syspnp->currentItem()));
+ LogViewerPluginSettings::setSysSRC(getArg(m_syssrc->currentItem()));
+ LogViewerPluginSettings::setSysSCD(getArg(m_sysscd->currentItem()));
+ LogViewerPluginSettings::setSysSNF(getArg(m_syssnf->currentItem()));
+ LogViewerPluginSettings::setSysRSS(getArg(m_sysrss->currentItem()));
+ LogViewerPluginSettings::setSysWEB(getArg(m_sysweb->currentItem()));
+
+ LogViewerPluginSettings::setUseRichText(m_useRich->isChecked());
+
+
+ LogViewerPluginSettings::writeConfig();
+
+ LogFlags::instance().updateFlags();
+
+ return true;
+ }
+
+ int LogPrefWidget::getLevel(unsigned int arg)
+ {
+ switch(arg)
+ {
+ case 0x0F:
+ return 0;
+ case 0x07:
+ return 1;
+ case 0x03:
+ return 2;
+ case 0x01:
+ return 3;
+ case 0x00:
+ return 4;
+ default:
+ return 0;
+ }
+ }
+
+ unsigned int LogPrefWidget::getArg(int level)
+ {
+ switch(level)
+ {
+ case 0:
+ return 0x0F;
+ case 1:
+ return 0x07;
+ case 2:
+ return 0x03;
+ case 3:
+ return 0x01;
+ case 4:
+ return 0x00;
+ default:
+ return 0;
+ }
+ }
+}
+
+#include "logprefwidget.moc"
diff --git a/plugins/logviewer/logprefwidget.h b/plugins/logviewer/logprefwidget.h
new file mode 100644
index 0000000..35a6c06
--- /dev/null
+++ b/plugins/logviewer/logprefwidget.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasic *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef LOGPREFWIDGET_H
+#define LOGPREFWIDGET_H
+
+#include "logprefwidgetbase.h"
+
+namespace kt
+{
+ class LogPrefWidget: public LogPrefWidgetBase
+ {
+ Q_OBJECT
+ public:
+ LogPrefWidget(QWidget *parent = 0, const char *name = 0);
+ bool apply();
+
+ private:
+ int getLevel(unsigned int arg);
+ unsigned int getArg(int level);
+ };
+}
+#endif
diff --git a/plugins/logviewer/logprefwidgetbase.ui b/plugins/logviewer/logprefwidgetbase.ui
new file mode 100644
index 0000000..76e7124
--- /dev/null
+++ b/plugins/logviewer/logprefwidgetbase.ui
@@ -0,0 +1,648 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>LogPrefWidgetBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>LogPrefWidgetBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>LogViewer Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>110</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QButtonGroup" row="0" column="1">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="title">
+ <string>Plugins</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_sysipf</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>IPFilter:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1_5</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_syssrc</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Search:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1_6</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="2" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_syspnp</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel2_3</cstring>
+ </property>
+ <property name="text">
+ <string>UPnP:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1_7</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel2_4</cstring>
+ </property>
+ <property name="text">
+ <string>ScanFolder:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1_8</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="3" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_syssnf</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="4" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_sysscd</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel2_5</cstring>
+ </property>
+ <property name="text">
+ <string>Scheduler:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1_9</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="5" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_syspfi</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel2_6</cstring>
+ </property>
+ <property name="text">
+ <string>PartFileImport:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1_10</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>textLabel2_7</cstring>
+ </property>
+ <property name="text">
+ <string>InfoWidget:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1_11</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="6" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_sysinw</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="0">
+ <property name="name">
+ <cstring>textLabel2_7_2</cstring>
+ </property>
+ <property name="text">
+ <string>RSS plugin:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1_11</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="7" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_sysrss</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="8" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_sysweb</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="8" column="0">
+ <property name="name">
+ <cstring>textLabel2_7_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>WebInterface plugin:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1_11</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup" row="0" column="0">
+ <property name="name">
+ <cstring>buttonGroup3</cstring>
+ </property>
+ <property name="title">
+ <string>System</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>General:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2_8</cstring>
+ </property>
+ <property name="text">
+ <string>Connections:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel2_9</cstring>
+ </property>
+ <property name="text">
+ <string>DHT:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel2_10</cstring>
+ </property>
+ <property name="text">
+ <string>Tracker:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_sysgen</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_syscon</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="2" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_sysdht</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Disk I/O:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="3" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_systrk</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="4" column="1">
+ <item>
+ <property name="text">
+ <string>All</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Notice</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Important</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_sysdio</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>buttonGroup3_2</cstring>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>m_useRich</cstring>
+ </property>
+ <property name="text">
+ <string>Use rich te&amp;xt for log</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/plugins/logviewer/logviewer.cpp b/plugins/logviewer/logviewer.cpp
new file mode 100644
index 0000000..5e6b16c
--- /dev/null
+++ b/plugins/logviewer/logviewer.cpp
@@ -0,0 +1,110 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kglobal.h>
+#include <kconfig.h>
+#include <qapplication.h>
+#include "logviewer.h"
+#include "logflags.h"
+#include "logviewerpluginsettings.h"
+
+namespace kt
+{
+ const int LOG_EVENT_TYPE = 65432;
+
+ class LogEvent : public QCustomEvent
+ {
+ QString str;
+ public:
+ LogEvent(const QString & str) : QCustomEvent(LOG_EVENT_TYPE),str(str)
+ {}
+
+ virtual ~LogEvent()
+ {}
+
+ const QString & msg() const {return str;}
+ };
+
+ LogViewer::LogViewer(QWidget *parent, const char *name)
+ : KTextBrowser(parent, name), LogMonitorInterface()
+ {
+ /*
+ IMPORTANT: use LogText mode, so that setMaxLogLines will work, if not everything will be kept in memory.
+ */
+ setTextFormat(Qt::LogText);
+ setMaxLogLines(200);
+ setMinimumSize(QSize(0,50));
+ setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
+ KGlobal::config()->setGroup("LogViewer");
+ if (KGlobal::config()->hasKey("LogViewerWidgetSize"))
+ {
+ QSize s = KGlobal::config()->readSizeEntry("LogViewerWidgetSize",0);
+ resize(s);
+ }
+
+ LogFlags::instance().setLog(this);
+ }
+
+
+ LogViewer::~LogViewer()
+ {
+ KGlobal::config()->setGroup("LogViewer");
+ KGlobal::config()->writeEntry("LogViewerWidgetSize",size());
+ LogFlags::instance().setLog(0);
+ }
+
+
+ void LogViewer::message(const QString& line, unsigned int arg)
+ {
+ /*
+ IMPORTANT: because QTextBrowser is not thread safe, we must use the Qt event mechanism
+ to add strings to it, this will ensure that strings will only be added in the main application
+ thread.
+ */
+ if(arg==0x00 || LogFlags::instance().checkFlags(arg))
+ {
+ if(m_useRichText)
+ {
+ QString tmp = line;
+ LogEvent* le = new LogEvent(LogFlags::instance().getFormattedMessage(arg, tmp));
+ QApplication::postEvent(this,le);
+ }
+ else
+ {
+ LogEvent* le = new LogEvent(line);
+ QApplication::postEvent(this,le);
+ }
+ }
+ }
+
+ void LogViewer::customEvent(QCustomEvent* ev)
+ {
+ if (ev->type() == LOG_EVENT_TYPE)
+ {
+ LogEvent* le = (LogEvent*)ev;
+ append(le->msg());
+ }
+ }
+
+ void LogViewer::setRichText(bool val)
+ {
+ m_useRichText = val;
+ }
+}
+#include "logviewer.moc"
diff --git a/plugins/logviewer/logviewer.h b/plugins/logviewer/logviewer.h
new file mode 100644
index 0000000..4198215
--- /dev/null
+++ b/plugins/logviewer/logviewer.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTLOGVIEWER_H
+#define KTLOGVIEWER_H
+
+#include <ktextbrowser.h>
+#include <interfaces/logmonitorinterface.h>
+#include "logflags.h"
+
+namespace kt
+{
+ /**
+ * @author Joris Guisson
+ */
+ class LogViewer : public KTextBrowser, public LogMonitorInterface
+ {
+ Q_OBJECT
+ public:
+ LogViewer(QWidget *parent = 0, const char *name = 0);
+ virtual ~LogViewer();
+
+ virtual void message(const QString& line, unsigned int arg);
+ virtual void customEvent(QCustomEvent* ev);
+
+ void setRichText(bool val);
+
+ private:
+ bool m_useRichText;
+ };
+
+}
+
+#endif
diff --git a/plugins/logviewer/logviewerplugin.cpp b/plugins/logviewer/logviewerplugin.cpp
new file mode 100644
index 0000000..aa35d2f
--- /dev/null
+++ b/plugins/logviewer/logviewerplugin.cpp
@@ -0,0 +1,84 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <interfaces/guiinterface.h>
+#include "logviewerplugin.h"
+#include "logviewer.h"
+#include "logprefpage.h"
+#include "logflags.h"
+
+#define NAME "Log Viewer"
+#define AUTHOR "Joris Guisson"
+#define EMAIL "joris.guisson@gmail.com"
+
+using namespace bt;
+
+K_EXPORT_COMPONENT_FACTORY(ktlogviewerplugin,KGenericFactory<kt::LogViewerPlugin>("ktlogviewerplugin"))
+
+namespace kt
+{
+
+ LogViewerPlugin::LogViewerPlugin(QObject* parent, const char* qt_name, const QStringList& args)
+ : Plugin(parent, qt_name, args, NAME,i18n("Log Viewer"), AUTHOR, EMAIL,
+ i18n("Displays ktorrent logging output"),"log")
+ {
+ lv = 0;
+ }
+
+
+ LogViewerPlugin::~LogViewerPlugin()
+ {}
+
+
+ void LogViewerPlugin::load()
+ {
+ lv = new LogViewer();
+ this->getGUI()->addToolWidget(lv,"log",i18n("Log Viewer"),GUIInterface::DOCK_BOTTOM);
+ bt::Log & lg = Globals::instance().getLog(0);
+ lg.addMonitor(lv);
+ pref = new LogPrefPage();
+ this->getGUI()->addPrefPage(pref);
+ }
+
+ void LogViewerPlugin::unload()
+ {
+ this->getGUI()->removeToolWidget(lv);
+ bt::Log & lg = Globals::instance().getLog(0);
+ lg.removeMonitor(lv);
+ delete lv;
+ lv = 0;
+ this->getGUI()->removePrefPage(pref);
+ delete pref;
+ pref = 0;
+ LogFlags::finalize();
+ }
+
+ bool LogViewerPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+
+}
+#include "logviewerplugin.moc"
diff --git a/plugins/logviewer/logviewerplugin.h b/plugins/logviewer/logviewerplugin.h
new file mode 100644
index 0000000..9cd5af8
--- /dev/null
+++ b/plugins/logviewer/logviewerplugin.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTLOGVIEWERPLUGIN_H
+#define KTLOGVIEWERPLUGIN_H
+
+#include <interfaces/plugin.h>
+
+
+
+namespace kt
+{
+ class LogViewer;
+ class LogPrefPage;
+
+ /**
+ * @author Joris Guisson
+ */
+ class LogViewerPlugin : public Plugin
+ {
+ Q_OBJECT
+ public:
+ LogViewerPlugin(QObject* parent, const char* qt_name, const QStringList& args);
+ virtual ~LogViewerPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual bool versionCheck(const QString& version) const;
+ private:
+ LogViewer* lv;
+ LogPrefPage* pref;
+ };
+
+}
+
+#endif
diff --git a/plugins/logviewer/logviewerpluginsettings.kcfgc b/plugins/logviewer/logviewerpluginsettings.kcfgc
new file mode 100644
index 0000000..c10fa1c
--- /dev/null
+++ b/plugins/logviewer/logviewerpluginsettings.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=ktlogviewerplugin.kcfg
+ClassName=LogViewerPluginSettings
+Namespace=kt
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables \ No newline at end of file
diff --git a/plugins/partfileimport/Makefile.am b/plugins/partfileimport/Makefile.am
new file mode 100644
index 0000000..f5aca48
--- /dev/null
+++ b/plugins/partfileimport/Makefile.am
@@ -0,0 +1,27 @@
+INCLUDES = -I$(top_builddir)/apps/ktorrent -I$(srcdir)/../../libktorrent \
+ $(all_includes)
+METASOURCES = AUTO
+kde_module_LTLIBRARIES = ktpartfileimportplugin.la
+noinst_HEADERS = partfileimportplugin.h importdialog.h
+ktpartfileimportplugin_la_SOURCES = partfileimportplugin.cpp importdlgbase.ui importdialog.cpp
+
+# Libs needed by the plugin
+ktpartfileimportplugin_la_LIBADD = $(LIB_KHTML) $(LIB_KPARTS) \
+ ../../libktorrent/libktorrent.la \
+ $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KFILE)
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktpartfileimportplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+# rc file containing the GUI for the plugin
+rcdir = $(kde_datadir)/ktorrent
+rc_DATA = ktpartfileimportpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+kde_services_DATA = ktpartfileimportplugin.desktop
+
+# kde_kcfg_DATA = ktpartfileimportplugin.kcfg
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/partfileimport/importdialog.cpp b/plugins/partfileimport/importdialog.cpp
new file mode 100644
index 0000000..22f9a4b
--- /dev/null
+++ b/plugins/partfileimport/importdialog.cpp
@@ -0,0 +1,389 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kurl.h>
+#include <klocale.h>
+#include <kprogress.h>
+#include <kurlrequester.h>
+#include <kpushbutton.h>
+#include <kmessagebox.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+#include <util/log.h>
+#include <util/error.h>
+#include <util/file.h>
+#include <util/fileops.h>
+#include <util/functions.h>
+#include <torrent/globals.h>
+#include <torrent/torrent.h>
+#include <torrent/chunkmanager.h>
+#include <interfaces/coreinterface.h>
+#include "importdialog.h"
+#include <datachecker/singledatachecker.h>
+#include <datachecker/multidatachecker.h>
+
+using namespace bt;
+
+namespace kt
+{
+ ImportDialog::ImportDialog(CoreInterface* core,QWidget* parent, const char* name, bool modal, WFlags fl)
+ : ImportDlgBase(parent,name, modal,fl),DataCheckerListener(false),core(core)
+ {
+ KURLRequester* r = m_torrent_url;
+ r->setMode(KFile::File|KFile::LocalOnly);
+ r->setFilter("*.torrent|" + i18n("Torrent files") + "\n*|" + i18n("All files"));
+
+ r = m_data_url;
+ r->setMode(KFile::File|KFile::Directory|KFile::LocalOnly);
+
+ connect(m_import_btn,SIGNAL(clicked()),this,SLOT(onImport()));
+ connect(m_cancel_btn,SIGNAL(clicked()),this,SLOT(reject()));
+ m_progress->setEnabled(false);
+ }
+
+ ImportDialog::~ImportDialog()
+ {}
+
+ void ImportDialog::progress(Uint32 num,Uint32 total)
+ {
+ m_progress->setTotalSteps(total);
+ m_progress->setProgress(num);
+ }
+
+ void ImportDialog::status(Uint32 ,Uint32 )
+ {
+ // don't care
+ }
+
+ void ImportDialog::finished()
+ {
+ // only used for check in separate thread, so does not apply for the import plugin
+ }
+
+ void ImportDialog::import(Torrent & tor)
+ {
+ // get the urls
+ KURL tor_url = KURL::fromPathOrURL(m_torrent_url->url());
+ KURL data_url = KURL::fromPathOrURL(m_data_url->url());
+
+ // now we need to check the data
+ DataChecker* dc = 0;
+ if (tor.isMultiFile())
+ dc = new MultiDataChecker();
+ else
+ dc = new SingleDataChecker();
+
+ try
+ {
+ dc->setListener(this);
+ dc->check(data_url.path(),tor,QString::null);
+ }
+ catch (Error & e)
+ {
+ delete dc;
+ KMessageBox::error(this,i18n("Cannot verify data : %1").arg(e.toString()),i18n("Error"));
+ reject();
+ return;
+ }
+
+ // find a new torrent dir and make it if necessary
+ QString tor_dir = core->findNewTorrentDir();
+ if (!tor_dir.endsWith(bt::DirSeparator()))
+ tor_dir += bt::DirSeparator();
+
+ try
+ {
+ if (!bt::Exists(tor_dir))
+ bt::MakeDir(tor_dir);
+
+ // write the index file
+ writeIndex(tor_dir + "index",dc->getDownloaded());
+
+ // copy the torrent file
+ bt::CopyFile(tor_url.prettyURL(),tor_dir + "torrent");
+
+ Uint64 imported = calcImportedBytes(dc->getDownloaded(),tor);
+
+ // make the cache
+ if (tor.isMultiFile())
+ {
+ QValueList<Uint32> dnd_files;
+ bool dnd = false;
+ // first make tor_dir/cache/
+ QString cache_dir = tor_dir + "cache" + bt::DirSeparator();
+ QString dnd_dir = tor_dir + "dnd" + bt::DirSeparator();
+ if (!bt::Exists(cache_dir))
+ MakeDir(cache_dir);
+ if (!bt::Exists(dnd_dir))
+ MakeDir(dnd_dir);
+
+
+ // make all sub symlinks
+ for (Uint32 i = 0;i < tor.getNumFiles();i++)
+ {
+ linkTorFile(cache_dir,dnd_dir,data_url,tor.getFile(i).getPath(),dnd);
+ if (dnd)
+ dnd_files.append(i);
+ dnd = false;
+ }
+
+ QString durl = data_url.path();
+ if (durl.endsWith(bt::DirSeparator()))
+ durl = durl.left(durl.length() - 1);
+ int ds = durl.findRev(bt::DirSeparator());
+ if (durl.mid(ds+1) == tor.getNameSuggestion())
+ {
+ durl = durl.left(ds);
+ saveStats(tor_dir + "stats",KURL::fromPathOrURL(durl),imported,false);
+ }
+ else
+ {
+ saveStats(tor_dir + "stats",KURL::fromPathOrURL(durl),imported,true);
+ }
+ saveFileInfo(tor_dir + "file_info",dnd_files);
+ }
+ else
+ {
+ // single file, just symlink the data_url to tor_dir/cache
+ bt::SymLink(data_url.path(),tor_dir + "cache");
+ QString durl = data_url.path();
+ int ds = durl.findRev(bt::DirSeparator());
+ durl = durl.left(ds);
+ saveStats(tor_dir + "stats",durl,imported,false);
+ }
+
+ // everything went OK, so load the whole shabang and start downloading
+ core->loadExistingTorrent(tor_dir);
+ }
+ catch (Error & e)
+ {
+ // delete tor_dir
+ bt::Delete(tor_dir,true);
+ delete dc;
+ KMessageBox::error(this,e.toString(),i18n("Error"));
+ reject();
+ return;
+ }
+
+ delete dc;
+ accept();
+ }
+
+ void ImportDialog::onTorrentGetReult(KIO::Job* j)
+ {
+ if (j->error())
+ {
+ j->showErrorDialog(this);
+ reject();
+ }
+ else
+ {
+ KIO::StoredTransferJob* stj = (KIO::StoredTransferJob*)j;
+ Torrent tor;
+
+ // try to load the torrent
+ try
+ {
+ tor.load(stj->data(),false);
+ }
+ catch (Error & e)
+ {
+ KMessageBox::error(this,i18n("Cannot load the torrent file : %1").arg(e.toString()),
+ i18n("Error"));
+ reject();
+ return;
+ }
+ import(tor);
+ }
+ }
+
+ void ImportDialog::onImport()
+ {
+ m_progress->setEnabled(true);
+ m_import_btn->setEnabled(false);
+ m_cancel_btn->setEnabled(false);
+ m_torrent_url->setEnabled(false);
+ m_data_url->setEnabled(false);
+
+ KURL tor_url = KURL::fromPathOrURL(m_torrent_url->url());
+ if (!tor_url.isLocalFile())
+ {
+ // download the torrent file
+ KIO::StoredTransferJob* j = KIO::storedGet(tor_url);
+ connect(j,SIGNAL(result(KIO::Job* )),this,SLOT(onTorrentGetReult(KIO::Job*)));
+ }
+ else
+ {
+ KURL tor_url = KURL::fromPathOrURL(m_torrent_url->url());
+ Torrent tor;
+
+ // try to load the torrent
+ try
+ {
+ tor.load(tor_url.path(),false);
+ }
+ catch (Error & e)
+ {
+ KMessageBox::error(this,i18n("Cannot load the torrent file : %1").arg(e.toString()),
+ i18n("Error"));
+ reject();
+ return;
+ }
+ import(tor);
+ }
+ }
+
+ void ImportDialog::writeIndex(const QString & file,const BitSet & chunks)
+ {
+ // first try to open it
+ File fptr;
+ if (!fptr.open(file,"wb"))
+ throw Error(i18n("Cannot open %1 : %2").arg(file).arg(fptr.errorString()));
+
+ // write all chunks to the file
+ for (Uint32 i = 0;i < chunks.getNumBits();i++)
+ {
+ if (!chunks.get(i))
+ continue;
+
+ // we have the chunk so write a NewChunkHeader struct to the file
+ NewChunkHeader hdr;
+ hdr.index = i;
+ hdr.deprecated = 0;
+ fptr.write(&hdr,sizeof(NewChunkHeader));
+ }
+ }
+
+ void ImportDialog::linkTorFile(const QString & cache_dir,const QString & dnd_dir,
+ const KURL & data_url,const QString & fpath,bool & dnd)
+ {
+ QStringList sl = QStringList::split(bt::DirSeparator(),fpath);
+
+ // create all necessary subdirs
+ QString ctmp = cache_dir;
+ QString otmp = data_url.path();
+ if (!otmp.endsWith(bt::DirSeparator()))
+ otmp += bt::DirSeparator();
+
+ QString dtmp = dnd_dir;
+ for (Uint32 i = 0;i < sl.count() - 1;i++)
+ {
+ otmp += sl[i];
+ ctmp += sl[i];
+ dtmp += sl[i];
+ // we need to make the same directory structure in the cache
+ // as the output dir
+ if (!bt::Exists(ctmp))
+ MakeDir(ctmp);
+ if (!bt::Exists(otmp))
+ MakeDir(otmp);
+ if (!bt::Exists(dtmp))
+ MakeDir(dtmp);
+ otmp += bt::DirSeparator();
+ ctmp += bt::DirSeparator();
+ dtmp += bt::DirSeparator();
+ }
+
+ QString dfile = otmp + sl.last();
+ if (!bt::Exists(dfile))
+ {
+ // when we start the torrent the user will be asked what to do
+ // bt::SymLink(dfile,cache_dir + fpath);
+ dnd = false;
+ }
+ else
+ {
+ // just symlink the existing file
+ bt::SymLink(dfile,cache_dir + fpath);
+ dnd = false;
+ }
+ }
+
+ void ImportDialog::saveStats(const QString & stats_file,const KURL & data_url,Uint64 imported,bool custom_output_name)
+ {
+ QFile fptr(stats_file);
+ if (!fptr.open(IO_WriteOnly))
+ {
+ Out(SYS_PFI|LOG_IMPORTANT) << "Warning : can't create stats file" << endl;
+ return;
+ }
+
+ QTextStream out(&fptr);
+ out << "OUTPUTDIR=" << data_url.path() << ::endl;
+ out << "UPLOADED=0" << ::endl;
+ out << "RUNNING_TIME_DL=0" << ::endl;
+ out << "RUNNING_TIME_UL=0" << ::endl;
+ out << "PRIORITY=0" << ::endl;
+ out << "AUTOSTART=1" << ::endl;
+ if (core->getGlobalMaxShareRatio() > 0)
+ out << QString("MAX_RATIO=%1").arg(core->getGlobalMaxShareRatio(),0,'f',2) << ::endl;
+ out << QString("IMPORTED=%1").arg(imported) << ::endl;
+ if (custom_output_name)
+ out << "CUSTOM_OUTPUT_NAME=1" << endl;
+ }
+
+ Uint64 ImportDialog::calcImportedBytes(const bt::BitSet & chunks,const Torrent & tor)
+ {
+ Uint64 nb = 0;
+ Uint64 ls = tor.getFileLength() % tor.getChunkSize();
+ if (ls == 0)
+ ls = tor.getChunkSize();
+
+ for (Uint32 i = 0;i < chunks.getNumBits();i++)
+ {
+ if (!chunks.get(i))
+ continue;
+
+ if (i == chunks.getNumBits() - 1)
+ nb += ls;
+ else
+ nb += tor.getChunkSize();
+ }
+ return nb;
+ }
+
+ void ImportDialog::saveFileInfo(const QString & file_info_file,QValueList<Uint32> & dnd)
+ {
+ // saves which TorrentFile's do not need to be downloaded
+ File fptr;
+ if (!fptr.open(file_info_file,"wb"))
+ {
+ Out(SYS_PFI|LOG_IMPORTANT) << "Warning : Can't save chunk_info file : " << fptr.errorString() << endl;
+ return;
+ }
+
+ ;
+
+ // first write the number of excluded ones
+ Uint32 tmp = dnd.count();
+ fptr.write(&tmp,sizeof(Uint32));
+ // then all the excluded ones
+ for (Uint32 i = 0;i < dnd.count();i++)
+ {
+ tmp = dnd[i];
+ fptr.write(&tmp,sizeof(Uint32));
+ }
+ fptr.flush();
+ }
+}
+
+
+
+#include "importdialog.moc"
+
diff --git a/plugins/partfileimport/importdialog.h b/plugins/partfileimport/importdialog.h
new file mode 100644
index 0000000..b7617fc
--- /dev/null
+++ b/plugins/partfileimport/importdialog.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef IMPORTDIALOG_H
+#define IMPORTDIALOG_H
+
+#include <util/constants.h>
+#include <datachecker/datacheckerlistener.h>
+#include "importdlgbase.h"
+
+class KURL;
+
+namespace bt
+{
+ class BitSet;
+ class Torrent;
+}
+
+namespace KIO
+{
+ class Job;
+}
+
+
+namespace kt
+{
+ class CoreInterface;
+
+ class ImportDialog : public ImportDlgBase,public bt::DataCheckerListener
+ {
+ Q_OBJECT
+
+ public:
+ ImportDialog(CoreInterface* core,QWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags fl = 0 );
+ virtual ~ImportDialog();
+
+ public slots:
+ void onImport();
+ void onTorrentGetReult(KIO::Job* j);
+
+ private:
+ void writeIndex(const QString & file,const bt::BitSet & chunks);
+ void linkTorFile(const QString & cache_dir,const QString & dnd_dir,
+ const KURL & data_url,const QString & fpath,bool & dnd);
+ void saveStats(const QString & stats_file,const KURL & data_url,bt::Uint64 imported,bool custom_output_name);
+ bt::Uint64 calcImportedBytes(const bt::BitSet & chunks,const bt::Torrent & tor);
+ void saveFileInfo(const QString & file_info_file,QValueList<bt::Uint32> & dnd);
+
+ virtual void progress(bt::Uint32 num,bt::Uint32 total);
+ virtual void status(bt::Uint32 num_failed,bt::Uint32 num_downloaded);
+ virtual void finished();
+
+ void import(bt::Torrent & tor);
+
+ private:
+ CoreInterface* core;
+ };
+}
+
+#endif
+
diff --git a/plugins/partfileimport/importdlgbase.ui b/plugins/partfileimport/importdlgbase.ui
new file mode 100644
index 0000000..e1cdd64
--- /dev/null
+++ b/plugins/partfileimport/importdlgbase.ui
@@ -0,0 +1,163 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ImportDlgBase</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>ImportDlgBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>473</width>
+ <height>196</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Import an existing download</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>60</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Torrent:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>60</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Data:</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>m_torrent_url</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>m_data_url</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KActiveLabel">
+ <property name="name">
+ <cstring>kActiveLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Please specify the torrent and the data already downloaded for that torrent.</string>
+ </property>
+ </widget>
+ <widget class="KProgress">
+ <property name="name">
+ <cstring>m_progress</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>61</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_import_btn</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Import</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_cancel_btn</cstring>
+ </property>
+ <property name="text">
+ <string>Ca&amp;ncel</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kactivelabel.h</includehint>
+ <includehint>kprogress.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/partfileimport/ktpartfileimportplugin.desktop b/plugins/partfileimport/ktpartfileimportplugin.desktop
new file mode 100644
index 0000000..5b28fe0
--- /dev/null
+++ b/plugins/partfileimport/ktpartfileimportplugin.desktop
@@ -0,0 +1,25 @@
+[Desktop Entry]
+Name=PartialFileImportPlugin
+Name[bg]=Приставка за частично импортиране на файлове
+Name[de]=Teildatei-Import-Modul
+Name[el]=Πρόσθετο εισαγωγής μερικής λήψης
+Name[es]=Importación de ficheros parciales
+Name[et]=Osalise faili impordi plugin
+Name[it]=Plugin importazione file parziali
+Name[nb]=Filimportmodul
+Name[nds]=Importmoduul för Deeldateien
+Name[nl]=Import gedeeltelijke bestanden
+Name[pl]=Wtyczka importu części plików
+Name[pt]=Importação de Ficheiros Parciais
+Name[pt_BR]=Plugin de Importação Parcial de Arquivo
+Name[sk]=PartialFileImport Plugin
+Name[sr]=Прикључак за увоз недовршених фајлова
+Name[sr@Latn]=Priključak za uvoz nedovršenih fajlova
+Name[sv]=Insticksprogram för partiell filimport
+Name[tr]=Tamamlanmamış Dosya Alma Eklentisi
+Name[xx]=xxPartialFileImportPluginxx
+Name[zh_CN]=部分文件导入插件
+Name[zh_TW]=PartialFileImport外掛程式
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktpartfileimportplugin
diff --git a/plugins/partfileimport/ktpartfileimportpluginui.rc b/plugins/partfileimport/ktpartfileimportpluginui.rc
new file mode 100644
index 0000000..f8867e3
--- /dev/null
+++ b/plugins/partfileimport/ktpartfileimportpluginui.rc
@@ -0,0 +1,8 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="ktorrent" version="1">
+<MenuBar>
+ <Menu name="file">
+ <Action name="partfileimport"/>
+ </Menu>
+</MenuBar>
+</kpartgui>
diff --git a/plugins/partfileimport/partfileimportplugin.cpp b/plugins/partfileimport/partfileimportplugin.cpp
new file mode 100644
index 0000000..c154478
--- /dev/null
+++ b/plugins/partfileimport/partfileimportplugin.cpp
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kstdaction.h>
+#include <kpopupmenu.h>
+#include <interfaces/guiinterface.h>
+#include <interfaces/coreinterface.h>
+#include "partfileimportplugin.h"
+#include "importdialog.h"
+
+#define NAME "Import"
+#define AUTHOR "Joris Guisson"
+#define EMAIL "joris.guisson@gmail.com"
+
+
+
+K_EXPORT_COMPONENT_FACTORY(ktpartfileimportplugin,KGenericFactory<kt::PartFileImportPlugin>("ktpartfileimportplugin"))
+
+namespace kt
+{
+
+ PartFileImportPlugin::PartFileImportPlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name, args,NAME,i18n("Import"),AUTHOR,EMAIL,i18n("Imports partially or fully downloaded torrents from other clients"),"ktplugins")
+ {
+ setXMLFile("ktpartfileimportpluginui.rc");
+ import_action = 0;
+ }
+
+
+ PartFileImportPlugin::~PartFileImportPlugin()
+ {}
+
+
+ void PartFileImportPlugin::load()
+ {
+ import_action = new KAction(i18n("Import existing download" ), 0, this,
+ SLOT(onImport()), actionCollection(), "partfileimport" );
+ }
+
+ void PartFileImportPlugin::unload()
+ {
+ delete import_action;
+ import_action = 0;
+ }
+
+ void PartFileImportPlugin::onImport()
+ {
+ ImportDialog dlg(getCore(),0,0,true);
+ dlg.exec();
+ }
+
+ bool PartFileImportPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+
+}
+#include "partfileimportplugin.moc"
diff --git a/plugins/partfileimport/partfileimportplugin.h b/plugins/partfileimport/partfileimportplugin.h
new file mode 100644
index 0000000..31f2d3a
--- /dev/null
+++ b/plugins/partfileimport/partfileimportplugin.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTPARTFILEIMPORTPLUGIN_H
+#define KTPARTFILEIMPORTPLUGIN_H
+
+#include <interfaces/plugin.h>
+
+class KAction;
+
+namespace kt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class PartFileImportPlugin : public Plugin
+ {
+ Q_OBJECT
+ public:
+ PartFileImportPlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~PartFileImportPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual bool versionCheck(const QString& version) const;
+ public slots:
+ void onImport();
+
+ private:
+ KAction* import_action;
+ };
+
+}
+
+#endif
diff --git a/plugins/rssfeed/Makefile.am b/plugins/rssfeed/Makefile.am
new file mode 100644
index 0000000..c295db5
--- /dev/null
+++ b/plugins/rssfeed/Makefile.am
@@ -0,0 +1,30 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+kde_module_LTLIBRARIES = ktrssfeedplugin.la
+
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktrssfeedplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+# rc file containing the GUI for the plugin
+# pluginsdir = $(kde_datadir)/ktrssfeedplugin
+# plugins_DATA = ktrssfeedpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+
+rcdir = $(kde_datadir)/ktorrent
+
+kde_kcfg_DATA = ktrssfeedplugin.kcfg
+kde_services_DATA = ktrssfeedplugin.desktop
+
+noinst_HEADERS = rssfeedplugin.h rssfeedmanager.h rssfeed.h rssfilter.h \
+ rssarticle.h rsslinkdownloader.h
+ktrssfeedplugin_la_SOURCES = rssfeedplugin.cpp rssfeedmanager.cpp \
+ rssfeedwidget.ui rssfeed.cpp rssfilter.cpp rssarticle.cpp rsslinkdownloader.cpp
+ktrssfeedplugin_la_LIBADD = $(LIB_KIO) $(LIB_QT) rss/librsslocal.la \
+ ../../libktorrent/libktorrent.la $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KHTML) $(LIB_KPARTS)
+
+SUBDIRS = rss
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/rssfeed/ktrssfeedplugin.desktop b/plugins/rssfeed/ktrssfeedplugin.desktop
new file mode 100644
index 0000000..a26c08b
--- /dev/null
+++ b/plugins/rssfeed/ktrssfeedplugin.desktop
@@ -0,0 +1,22 @@
+[Desktop Entry]
+Name=RssFeedPlugin
+Name[bg]=Приставка за RssFeed
+Name[de]=RSS-Nachrichtenquellen-Modul
+Name[el]=Πρόσθετο ροών Rss
+Name[et]=RSS-kanali plugin
+Name[it]=Plugin Fonti notizie
+Name[nb]=RSS-modul
+Name[nds]=RSS-Moduul
+Name[pl]=Wtyczka kanały RSS
+Name[pt_BR]=Plugin de RssFeed
+Name[sk]=RssFeed Plugin
+Name[sr]=Прикључак Rss довода
+Name[sr@Latn]=Priključak Rss dovoda
+Name[sv]=RSS-kanalinsticksprogram
+Name[tr]=RSS Besleme Eklentisi
+Name[uk]=Втулок подач RSS
+Name[xx]=xxRssFeedPluginxx
+Name[zh_CN]=RSS 种子插件
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktrssfeedplugin
diff --git a/plugins/rssfeed/ktrssfeedplugin.kcfg b/plugins/rssfeed/ktrssfeedplugin.kcfg
new file mode 100644
index 0000000..86499cb
--- /dev/null
+++ b/plugins/rssfeed/ktrssfeedplugin.kcfg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <kcfgfile name="ktrssfeedpluginrc"/>
+ <group name="general">
+ <entry name="default" type="Int">
+ <label>default</label>
+ <default>0</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/rssfeed/rss/COPYING b/plugins/rssfeed/rss/COPYING
new file mode 100644
index 0000000..cca2a5c
--- /dev/null
+++ b/plugins/rssfeed/rss/COPYING
@@ -0,0 +1,20 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/plugins/rssfeed/rss/Makefile.am b/plugins/rssfeed/rss/Makefile.am
new file mode 100644
index 0000000..75b570a
--- /dev/null
+++ b/plugins/rssfeed/rss/Makefile.am
@@ -0,0 +1,20 @@
+INCLUDES = \
+ -I$(top_srcdir)/src \
+ $(all_includes)
+
+noinst_LTLIBRARIES = \
+ librsslocal.la
+
+noinst_HEADERS = article.h document.h global.h image.h textinput.h \
+ loader.h librss.h
+
+librsslocal_la_SOURCES = article.cpp document.cpp image.cpp textinput.cpp \
+ tools_p.cpp loader.cpp
+
+librsslocal_la_METASOURCES = AUTO
+
+check_PROGRAMS = testlibrss
+testlibrss_SOURCES = testlibrss.cpp
+testlibrss_LDFLAGS = $(all_libraries)
+testlibrss_LDADD = librsslocal.la $(LIB_KIO)
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/rssfeed/rss/README b/plugins/rssfeed/rss/README
new file mode 100644
index 0000000..77d9450
--- /dev/null
+++ b/plugins/rssfeed/rss/README
@@ -0,0 +1,6 @@
+This is NOT original librss by Frerich Raabe, though based on it.
+
+This version is supposed to be called libsyndication but is not renamed to relieve packaging burden a bit
+(honestly, we just didn't yet get to it).
+
+Please DO NOT report any bugs about it to Frerich, since he most probably did not introduce the found bugs.
diff --git a/plugins/rssfeed/rss/article.cpp b/plugins/rssfeed/rss/article.cpp
new file mode 100644
index 0000000..571490e
--- /dev/null
+++ b/plugins/rssfeed/rss/article.cpp
@@ -0,0 +1,270 @@
+/*
+ * article.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "article.h"
+#include "tools_p.h"
+
+#include <kdebug.h>
+#include <krfcdate.h>
+#include <kurl.h>
+#include <kurllabel.h>
+#include <kmdcodec.h>
+
+#include <qdatetime.h>
+#include <qdom.h>
+
+using namespace RSS;
+namespace RSS
+{
+ KMD5 md5Machine;
+}
+
+struct Article::Private : public Shared
+{
+ QString title;
+ KURL link;
+ QString description;
+ QDateTime pubDate;
+ QString guid;
+ bool guidIsPermaLink;
+ MetaInfoMap meta;
+ KURL commentsLink;
+ int numComments;
+};
+
+Article::Article() : d(new Private)
+{
+}
+
+Article::Article(const Article &other) : d(0)
+{
+ *this = other;
+}
+
+Article::Article(const QDomNode &node, Format format) : d(new Private)
+{
+ QString elemText;
+
+ d->numComments=0;
+
+ if (!(elemText = extractNode(node, QString::fromLatin1("title"))).isNull())
+ d->title = elemText;
+
+
+ QDomNode n;
+ bool foundTorrentEnclosure = false;
+ for (n = node.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ const QDomElement e = n.toElement();
+ if ( (e.tagName()==QString::fromLatin1("enclosure") ) )
+ {
+ QString enclosureAttr = e.attribute(QString::fromLatin1("type"));
+ if (!enclosureAttr.isNull() )
+ {
+ if (enclosureAttr == "application/x-bittorrent")
+ {
+ enclosureAttr = e.attribute(QString::fromLatin1("url"));
+ if (!enclosureAttr.isNull() )
+ {
+ d->link=enclosureAttr;
+ foundTorrentEnclosure = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (!foundTorrentEnclosure)
+ {
+ if (format==AtomFeed)
+ {
+ QDomNode n;
+ for (n = node.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ const QDomElement e = n.toElement();
+ if ( (e.tagName()==QString::fromLatin1("link")) &&
+ (e.attribute(QString::fromLatin1("rel"))==QString::fromLatin1("alternate")))
+ {
+ d->link=n.toElement().attribute(QString::fromLatin1("href"));
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (!(elemText = extractNode(node, QString::fromLatin1("link"))).isNull())
+ d->link = elemText;
+ }
+ }
+
+
+ // prefer content/content:encoded over summary/description for feeds that provide it
+ QString tagName=(format==AtomFeed)? QString::fromLatin1("content"): QString::fromLatin1("content:encoded");
+
+ if (!(elemText = extractNode(node, tagName, false)).isNull())
+ d->description = elemText;
+
+ if (d->description.isEmpty())
+ {
+ if (!(elemText = extractNode(node, QString::fromLatin1("body"), false)).isNull())
+ d->description = elemText;
+
+ if (d->description.isEmpty()) // 3rd try: see http://www.intertwingly.net/blog/1299.html
+ {
+ if (!(elemText = extractNode(node, QString::fromLatin1((format==AtomFeed)? "summary" : "description"), false)).isNull())
+ d->description = elemText;
+ }
+ }
+
+ if (!(elemText = extractNode(node, QString::fromLatin1((format==AtomFeed)? "created": "pubDate"))).isNull())
+ {
+ time_t _time;
+ if (format==AtomFeed)
+ _time = parseISO8601Date(elemText);
+ else
+ _time = KRFCDate::parseDate(elemText);
+
+ // 0 means invalid, not epoch (it returns epoch+1 when it parsed epoch, see the KRFCDate::parseDate() docs)
+ if (_time != 0)
+ d->pubDate.setTime_t(_time);
+ }
+ if (!(elemText = extractNode(node, QString::fromLatin1("dc:date"))).isNull())
+ {
+ time_t _time = parseISO8601Date(elemText);
+
+ // 0 means invalid, not epoch (it returns epoch+1 when it parsed epoch, see the KRFCDate::parseDate() docs)
+ if (_time != 0)
+ d->pubDate.setTime_t(_time);
+ }
+
+ //no luck so far - so let's set it to the current time
+ if (!d->pubDate.isValid())
+ {
+ d->pubDate = QDateTime::currentDateTime();
+ }
+
+
+ if (!(elemText = extractNode(node, QString::fromLatin1("wfw:comment"))).isNull()) {
+ d->commentsLink = elemText;
+ }
+
+ if (!(elemText = extractNode(node, QString::fromLatin1("slash:comments"))).isNull()) {
+ d->numComments = elemText.toInt();
+ }
+
+ tagName=(format==AtomFeed)? QString::fromLatin1("id"): QString::fromLatin1("guid");
+ n = node.namedItem(tagName);
+ if (!n.isNull()) {
+ d->guidIsPermaLink = (format==AtomFeed)? false : true;
+ if (n.toElement().attribute(QString::fromLatin1("isPermaLink"), "true") == "false") d->guidIsPermaLink = false;
+
+ if (!(elemText = extractNode(node, tagName)).isNull())
+ d->guid = elemText;
+ }
+
+ if(d->guid.isEmpty()) {
+ d->guidIsPermaLink = false;
+
+ md5Machine.reset();
+ QDomNode n(node);
+ md5Machine.update(d->title.utf8());
+ md5Machine.update(d->description.utf8());
+ d->guid = QString(md5Machine.hexDigest().data());
+ d->meta[QString::fromLatin1("guidIsHash")] = QString::fromLatin1("true");
+ }
+
+ for (QDomNode i = node.firstChild(); !i.isNull(); i = i.nextSibling())
+ {
+ if (i.isElement() && i.toElement().tagName() == QString::fromLatin1("metaInfo:meta"))
+ {
+ QString type = i.toElement().attribute(QString::fromLatin1("type"));
+ d->meta[type] = i.toElement().text();
+ }
+ }
+}
+
+Article::~Article()
+{
+ if (d->deref())
+ delete d;
+}
+
+QString Article::title() const
+{
+ return d->title;
+}
+
+const KURL &Article::link() const
+{
+ return d->link;
+}
+
+QString Article::description() const
+{
+ return d->description;
+}
+
+QString Article::guid() const
+{
+ return d->guid;
+}
+
+bool Article::guidIsPermaLink() const
+{
+ return d->guidIsPermaLink;
+}
+
+const QDateTime &Article::pubDate() const
+{
+ return d->pubDate;
+}
+
+const KURL &Article::commentsLink() const
+{
+ return d->commentsLink;
+}
+
+int Article::comments() const
+{
+ return d->numComments;
+}
+
+
+QString Article::meta(const QString &key) const
+{
+ return d->meta[key];
+}
+
+KURLLabel *Article::widget(QWidget *parent, const char *name) const
+{
+ KURLLabel *label = new KURLLabel(d->link.url(), d->title, parent, name);
+ label->setUseTips(true);
+ if (!d->description.isNull())
+ label->setTipText(d->description);
+
+ return label;
+}
+
+Article &Article::operator=(const Article &other)
+{
+ if (this != &other) {
+ other.d->ref();
+ if (d && d->deref())
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+bool Article::operator==(const Article &other) const
+{
+ return d->guid == other.guid();
+}
+
+// vim:noet:ts=4
diff --git a/plugins/rssfeed/rss/article.h b/plugins/rssfeed/rss/article.h
new file mode 100644
index 0000000..bab7a38
--- /dev/null
+++ b/plugins/rssfeed/rss/article.h
@@ -0,0 +1,159 @@
+/*
+ * article.h
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_ARTICLE_H
+#define LIBRSS_ARTICLE_H
+
+#include <qmap.h>
+
+#include "global.h"
+
+class QDateTime;
+class QDomNode;
+template <class> class QValueList;
+class QString;
+class QWidget;
+class KURL;
+class KURLLabel;
+
+namespace RSS
+{
+ /**
+ * Represents an article as stored in a RSS file. You don't have to
+ * instantiate one of these yourself, the common way to access instances
+ * is via Document::articles().
+ * @see Document::articles()
+ */
+ class Article
+ {
+ public:
+ /**
+ * A list of articles.
+ */
+ typedef QValueList<Article> List;
+
+ /**
+ * Default constructor.
+ */
+ Article();
+
+ /**
+ * Copy constructor.
+ * @param other The Article object to copy.
+ */
+ Article(const Article &other);
+
+ /**
+ * Constructs an Article from a piece of RSS markup.
+ * @param node A QDomNode which references the DOM leaf to be used
+ * for constructing the Article.
+ */
+ Article(const QDomNode &node, Format format);
+
+ /**
+ * Assignment operator.
+ * @param other The Article object to clone.
+ * @return A reference to the cloned Article object.
+ */
+ Article &operator=(const Article &other);
+
+ /**
+ * Compares two articles. Two articles are treated to be identical
+ * if all their properties (title, link, description etc.) are
+ * equal.
+ * @param other The article this article should be compared with.
+ * @return Whether the two articles are equal.
+ */
+ bool operator==(const Article &other) const;
+
+ /**
+ * Convenience method. Simply calls !operator==().
+ * @param other The article this article should be compared with.
+ * @return Whether the two articles are unequal.
+ */
+ bool operator!=(const Article &other) const { return !operator==(other); }
+
+ /**
+ * Destructor.
+ */
+ virtual ~Article();
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The headline of this article, or QString::null if
+ * no headline was available.
+ */
+ QString title() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A URL referencing the complete text for this article,
+ * or an empty KURL if no link was available.
+ * Note that the RSS 0.91 Specification dictates that URLs not
+ * starting with "http://" or "ftp://" are considered invalid.
+ */
+ const KURL &link() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A story synopsis, or QString::null if no description
+ * was available.
+ */
+ QString description() const;
+
+ /**
+ * RSS 2.0 and upwards
+ * @return An article GUID (globally unique identifier).
+ */
+ QString guid() const;
+
+ /**
+ * RSS 2.0 and upwards
+ * @return If this article GUID is permalink. Has no meaning when guid() is QString::null.
+ */
+ bool guidIsPermaLink() const;
+
+ /**
+ * RSS 2.0 and upwards
+ * @return The date when the article was published.
+ */
+ const QDateTime &pubDate() const;
+
+ const KURL &commentsLink() const;
+ int comments() const;
+
+ QString meta(const QString &key) const;
+
+ /**
+ * @param parent The parent widget for the KURLLabel.
+ * @param name A name for the widget which will be used internally.
+ * @return a widget (a KURLLabel in this case) for the Article.
+ * This makes building a user-interface which contains the
+ * information in this Article object more convenient.
+ * The returned KURLLabel's caption will be the title(), clicking
+ * on it will emit the URL link(), and it has a QToolTip attached
+ * to it which displays the description() (in case it has one,
+ * if there is no description, the URL which the label links to
+ * will be used).
+ * Note that you have to delete the KURLLabel object returned by
+ * this method yourself.
+ */
+ KURLLabel *widget(QWidget *parent = 0, const char *name = 0) const;
+
+ typedef QMap<QString, QString> MetaInfoMap;
+
+ private:
+ struct Private;
+ Private *d;
+ };
+}
+
+#endif // LIBRSS_ARTICLE_H
+// vim: noet:ts=4
diff --git a/plugins/rssfeed/rss/document.cpp b/plugins/rssfeed/rss/document.cpp
new file mode 100644
index 0000000..be353e4
--- /dev/null
+++ b/plugins/rssfeed/rss/document.cpp
@@ -0,0 +1,619 @@
+/*
+ * document.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ *
+ */
+#include "document.h"
+#include "article.h"
+#include "image.h"
+#include "textinput.h"
+#include "tools_p.h"
+
+#include <krfcdate.h>
+#include <kurl.h>
+
+#include <qdatetime.h>
+#include <qdom.h>
+#include <qptrlist.h>
+
+using namespace RSS;
+
+struct Document::Private : public Shared
+{
+ Private() : version(v0_90), image(NULL), textInput(NULL), language(en)
+ {
+ format=UnknownFormat;
+ valid=false;
+ ttl=-1;
+ }
+
+ ~Private()
+ {
+ delete textInput;
+ delete image;
+ }
+
+ Version version;
+ QString title;
+ QString description;
+ KURL link;
+ Image *image;
+ TextInput *textInput;
+ Article::List articles;
+ Language language;
+ Format format;
+ QString copyright;
+ QDateTime pubDate;
+ QDateTime lastBuildDate;
+ QString rating;
+ KURL docs;
+ int ttl;
+ QString managingEditor;
+ QString webMaster;
+ HourList skipHours;
+ DayList skipDays;
+ bool valid;
+};
+
+Document::Document() : d(new Private)
+{
+}
+
+Document::Document(const Document &other) : d(0)
+{
+ *this = other;
+}
+
+Document::Document(const QDomDocument &doc) : d(new Private)
+{
+ QString elemText;
+ QDomNode rootNode = doc.documentElement();
+
+ // Determine the version of the present RSS markup.
+ QString attr;
+
+ // we should probably check that it ISN'T feed or rss, rather than check if it is xhtml
+ if (rootNode.toElement().tagName()==QString::fromLatin1("html"))
+ d->valid=false;
+ else
+ d->valid=true;
+
+ attr = rootNode.toElement().attribute(QString::fromLatin1("version"), QString::null);
+ if (!attr.isNull()) {
+ if (rootNode.toElement().tagName()=="feed")
+ {
+ d->format=AtomFeed;
+ if (attr == QString::fromLatin1("0.3"))
+ d->version = vAtom_0_3;
+ else if (attr == QString::fromLatin1("0.2")) /* smt -> review */
+ d->version = vAtom_0_2;
+ else if (attr == QString::fromLatin1("0.1")) /* smt -> review */
+ d->version = vAtom_0_1;
+ }
+ else
+ {
+ d->format=RSSFeed;
+ if (attr == QString::fromLatin1("0.91"))
+ d->version = v0_91;
+ else if (attr == QString::fromLatin1("0.92"))
+ d->version = v0_92;
+ else if (attr == QString::fromLatin1("0.93"))
+ d->version = v0_93;
+ else if (attr == QString::fromLatin1("0.94"))
+ d->version = v0_94;
+ else if (attr.startsWith("2.0") || attr == QString::fromLatin1("2")) // http://www.breuls.org/rss puts 2.00 in version (BR #0000016)
+ d->version = v2_0;
+ }
+ }
+
+ if (d->format==UnknownFormat)
+ {
+ attr = rootNode.toElement().attribute(QString::fromLatin1("xmlns"), QString::null);
+ if (!attr.isNull()) {
+ /*
+ * Hardcoding these URLs is actually a bad idea, since the DTD doesn't
+ * dictate a specific namespace. Still, most RSS files seem to use
+ * these two, so I'll go for them now. If it turns out that many
+ * mirrors of this RSS namespace are in use, I'll probably have to
+ * distinguish the RSS versions by analyzing the relationship between
+ * the nodes.
+ */
+ if (attr == QString::fromLatin1("http://my.netscape.com/rdf/simple/0.9/")) {
+ d->format=RSSFeed;
+ d->version = v0_90;
+ }
+ else if (attr == QString::fromLatin1("http://purl.org/rss/1.0/")) {
+ d->format=RSSFeed;
+ d->version = v1_0;
+ }
+ }
+ }
+
+ QDomNode channelNode;
+
+ if (d->format == AtomFeed)
+ channelNode=rootNode;
+ else
+ channelNode=rootNode.namedItem(QString::fromLatin1("channel"));
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("title"))).isNull())
+ d->title = elemText;
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("description"))).isNull())
+ d->description = elemText;
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("link"))).isNull())
+ d->link = elemText;
+
+
+ /* This is ugly but necessary since RSS 0.90 and 1.0 have a different parent
+ * node for <image>, <textinput> and <item> than RSS 0.91-0.94 and RSS 2.0.
+ */
+ QDomNode parentNode;
+ if (d->version == v0_90 || d->version == v1_0 || d->format == AtomFeed)
+ parentNode = rootNode;
+ else
+ {
+ // following is a HACK for broken 0.91 feeds like xanga.com's
+ if (!rootNode.namedItem(QString::fromLatin1("item")).isNull())
+ parentNode = rootNode;
+ else
+ parentNode = channelNode;
+ }
+
+ // image and textinput aren't supported by Atom.. handle in case feed provides
+ QDomNode n = parentNode.namedItem(QString::fromLatin1("image"));
+ if (!n.isNull())
+ d->image = new Image(n);
+
+ n = parentNode.namedItem(QString::fromLatin1("textinput"));
+ if (!n.isNull())
+ d->textInput = new TextInput(n);
+
+ // Our (hopefully faster) version of elementsByTagName()
+ QString tagName;
+ if (d->format == AtomFeed)
+ tagName=QString::fromLatin1("entry");
+ else
+ tagName=QString::fromLatin1("item");
+
+ for (n = parentNode.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ const QDomElement e = n.toElement();
+ if (e.tagName() == tagName)
+ d->articles.append(Article(e, d->format));
+ }
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("copyright"))).isNull())
+ d->copyright = elemText;
+
+ if (d->format == AtomFeed)
+ elemText = rootNode.toElement().attribute(QString::fromLatin1("xml:lang"), QString::null);
+ else
+ elemText = extractNode(channelNode, QString::fromLatin1("language"));
+
+ if (!elemText.isNull()){
+ if (elemText == QString::fromLatin1("af"))
+ d->language = af;
+ else if (elemText == QString::fromLatin1("sq"))
+ d->language = sq;
+ else if (elemText == QString::fromLatin1("eu"))
+ d->language = eu;
+ else if (elemText == QString::fromLatin1("be"))
+ d->language = be;
+ else if (elemText == QString::fromLatin1("bg"))
+ d->language = bg;
+ else if (elemText == QString::fromLatin1("ca"))
+ d->language = ca;
+ else if (elemText == QString::fromLatin1("zh-cn"))
+ d->language = zh_cn;
+ else if (elemText == QString::fromLatin1("zh-tw"))
+ d->language = zh_tw;
+ else if (elemText == QString::fromLatin1("hr"))
+ d->language = hr;
+ else if (elemText == QString::fromLatin1("cs"))
+ d->language = cs;
+ else if (elemText == QString::fromLatin1("da"))
+ d->language = da;
+ else if (elemText == QString::fromLatin1("nl"))
+ d->language = nl;
+ else if (elemText == QString::fromLatin1("nl-be"))
+ d->language = nl_be;
+ else if (elemText == QString::fromLatin1("nl-nl"))
+ d->language = nl_nl;
+ else if (elemText == QString::fromLatin1("en"))
+ d->language = en;
+ else if (elemText == QString::fromLatin1("en-au"))
+ d->language = en_au;
+ else if (elemText == QString::fromLatin1("en-bz"))
+ d->language = en_bz;
+ else if (elemText == QString::fromLatin1("en-ca"))
+ d->language = en_ca;
+ else if (elemText == QString::fromLatin1("en-ie"))
+ d->language = en_ie;
+ else if (elemText == QString::fromLatin1("en-jm"))
+ d->language = en_jm;
+ else if (elemText == QString::fromLatin1("en-nz"))
+ d->language = en_nz;
+ else if (elemText == QString::fromLatin1("en-ph"))
+ d->language = en_ph;
+ else if (elemText == QString::fromLatin1("en-za"))
+ d->language = en_za;
+ else if (elemText == QString::fromLatin1("en-tt"))
+ d->language = en_tt;
+ else if (elemText == QString::fromLatin1("en-gb"))
+ d->language = en_gb;
+ else if (elemText == QString::fromLatin1("en-us"))
+ d->language = en_us;
+ else if (elemText == QString::fromLatin1("en-zw"))
+ d->language = en_zw;
+ else if (elemText == QString::fromLatin1("fo"))
+ d->language = fo;
+ else if (elemText == QString::fromLatin1("fi"))
+ d->language = fi;
+ else if (elemText == QString::fromLatin1("fr"))
+ d->language = fr;
+ else if (elemText == QString::fromLatin1("fr-be"))
+ d->language = fr_be;
+ else if (elemText == QString::fromLatin1("fr-ca"))
+ d->language = fr_ca;
+ else if (elemText == QString::fromLatin1("fr-fr"))
+ d->language = fr_fr;
+ else if (elemText == QString::fromLatin1("fr-lu"))
+ d->language = fr_lu;
+ else if (elemText == QString::fromLatin1("fr-mc"))
+ d->language = fr_mc;
+ else if (elemText == QString::fromLatin1("fr-ch"))
+ d->language = fr_ch;
+ else if (elemText == QString::fromLatin1("gl"))
+ d->language = gl;
+ else if (elemText == QString::fromLatin1("gd"))
+ d->language = gd;
+ else if (elemText == QString::fromLatin1("de"))
+ d->language = de;
+ else if (elemText == QString::fromLatin1("de-at"))
+ d->language = de_at;
+ else if (elemText == QString::fromLatin1("de-de"))
+ d->language = de_de;
+ else if (elemText == QString::fromLatin1("de-li"))
+ d->language = de_li;
+ else if (elemText == QString::fromLatin1("de-lu"))
+ d->language = de_lu;
+ else if (elemText == QString::fromLatin1("de-ch"))
+ d->language = de_ch;
+ else if (elemText == QString::fromLatin1("el"))
+ d->language = el;
+ else if (elemText == QString::fromLatin1("hu"))
+ d->language = hu;
+ else if (elemText == QString::fromLatin1("is"))
+ d->language = is;
+ else if (elemText == QString::fromLatin1("id"))
+ d->language = id;
+ else if (elemText == QString::fromLatin1("ga"))
+ d->language = ga;
+ else if (elemText == QString::fromLatin1("it"))
+ d->language = it;
+ else if (elemText == QString::fromLatin1("it-it"))
+ d->language = it_it;
+ else if (elemText == QString::fromLatin1("it-ch"))
+ d->language = it_ch;
+ else if (elemText == QString::fromLatin1("ja"))
+ d->language = ja;
+ else if (elemText == QString::fromLatin1("ko"))
+ d->language = ko;
+ else if (elemText == QString::fromLatin1("mk"))
+ d->language = mk;
+ else if (elemText == QString::fromLatin1("no"))
+ d->language = no;
+ else if (elemText == QString::fromLatin1("pl"))
+ d->language = pl;
+ else if (elemText == QString::fromLatin1("pt"))
+ d->language = pt;
+ else if (elemText == QString::fromLatin1("pt-br"))
+ d->language = pt_br;
+ else if (elemText == QString::fromLatin1("pt-pt"))
+ d->language = pt_pt;
+ else if (elemText == QString::fromLatin1("ro"))
+ d->language = ro;
+ else if (elemText == QString::fromLatin1("ro-mo"))
+ d->language = ro_mo;
+ else if (elemText == QString::fromLatin1("ro-ro"))
+ d->language = ro_ro;
+ else if (elemText == QString::fromLatin1("ru"))
+ d->language = ru;
+ else if (elemText == QString::fromLatin1("ru-mo"))
+ d->language = ru_mo;
+ else if (elemText == QString::fromLatin1("ru-ru"))
+ d->language = ru_ru;
+ else if (elemText == QString::fromLatin1("sr"))
+ d->language = sr;
+ else if (elemText == QString::fromLatin1("sk"))
+ d->language = sk;
+ else if (elemText == QString::fromLatin1("sl"))
+ d->language = sl;
+ else if (elemText == QString::fromLatin1("es"))
+ d->language = es;
+ else if (elemText == QString::fromLatin1("es-ar"))
+ d->language = es_ar;
+ else if (elemText == QString::fromLatin1("es-bo"))
+ d->language = es_bo;
+ else if (elemText == QString::fromLatin1("es-cl"))
+ d->language = es_cl;
+ else if (elemText == QString::fromLatin1("es-co"))
+ d->language = es_co;
+ else if (elemText == QString::fromLatin1("es-cr"))
+ d->language = es_cr;
+ else if (elemText == QString::fromLatin1("es-do"))
+ d->language = es_do;
+ else if (elemText == QString::fromLatin1("es-ec"))
+ d->language = es_ec;
+ else if (elemText == QString::fromLatin1("es-sv"))
+ d->language = es_sv;
+ else if (elemText == QString::fromLatin1("es-gt"))
+ d->language = es_gt;
+ else if (elemText == QString::fromLatin1("es-hn"))
+ d->language = es_hn;
+ else if (elemText == QString::fromLatin1("es-mx"))
+ d->language = es_mx;
+ else if (elemText == QString::fromLatin1("es-ni"))
+ d->language = es_ni;
+ else if (elemText == QString::fromLatin1("es-pa"))
+ d->language = es_pa;
+ else if (elemText == QString::fromLatin1("es-py"))
+ d->language = es_py;
+ else if (elemText == QString::fromLatin1("es-pe"))
+ d->language = es_pe;
+ else if (elemText == QString::fromLatin1("es-pr"))
+ d->language = es_pr;
+ else if (elemText == QString::fromLatin1("es-es"))
+ d->language = es_es;
+ else if (elemText == QString::fromLatin1("es-uy"))
+ d->language = es_uy;
+ else if (elemText == QString::fromLatin1("es-ve"))
+ d->language = es_ve;
+ else if (elemText == QString::fromLatin1("sv"))
+ d->language = sv;
+ else if (elemText == QString::fromLatin1("sv-fi"))
+ d->language = sv_fi;
+ else if (elemText == QString::fromLatin1("sv-se"))
+ d->language = sv_se;
+ else if (elemText == QString::fromLatin1("tr"))
+ d->language = tr;
+ else if (elemText == QString::fromLatin1("uk"))
+ d->language = uk;
+ else
+ d->language = UndefinedLanguage;
+ }
+
+ if (d->format == AtomFeed)
+ tagName=QString::fromLatin1("issued"); // atom doesn't specify this for feeds
+ // but some broken feeds do this
+ else
+ tagName=QString::fromLatin1("pubDate");
+
+ if (!(elemText = extractNode(channelNode, tagName)).isNull()) {
+ time_t _time;
+
+ if (d->format == AtomFeed)
+ _time=parseISO8601Date(elemText);
+ else
+ _time=KRFCDate::parseDate(elemText);
+ /* \bug This isn't really the right way since it will set the date to
+ * Jan 1 1970, 1:00:00 if the passed date was invalid; this means that
+ * we cannot distinguish between that date, and invalid values. :-/
+ */
+ d->pubDate.setTime_t(_time);
+ }
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("dc:date"))).isNull()) {
+ time_t _time = parseISO8601Date(elemText);
+ /* \bug This isn't really the right way since it will set the date to
+ * Jan 1 1970, 1:00:00 if the passed date was invalid; this means that
+ * we cannot distinguish between that date, and invalid values. :-/
+ */
+ d->pubDate.setTime_t(_time);
+ }
+
+ if (d->format == AtomFeed)
+ tagName=QString::fromLatin1("modified");
+ else
+ tagName=QString::fromLatin1("lastBuildDate");
+ if (!(elemText = extractNode(channelNode, tagName)).isNull()) {
+ time_t _time;
+ if (d->format == AtomFeed)
+ _time = parseISO8601Date(elemText);
+ else
+ _time = KRFCDate::parseDate(elemText);
+ d->lastBuildDate.setTime_t(_time);
+ }
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("rating"))).isNull())
+ d->rating = elemText;
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("docs"))).isNull())
+ d->docs = elemText;
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1((d->format == AtomFeed) ? "author" : "managingEditor"))).isNull())
+ d->managingEditor = elemText;
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("webMaster"))).isNull())
+ d->webMaster = elemText;
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("ttl"))).isNull())
+ d->ttl = elemText.toUInt();
+
+ n = channelNode.namedItem(QString::fromLatin1("skipHours"));
+ if (!n.isNull())
+ for (QDomElement e = n.firstChild().toElement(); !e.isNull(); e = e.nextSibling().toElement())
+ if (e.tagName() == QString::fromLatin1("hour"))
+ d->skipHours.append(e.text().toUInt());
+
+ n = channelNode.namedItem(QString::fromLatin1("skipDays"));
+ if (!n.isNull()) {
+ Day day;
+ QString elemText;
+ for (QDomElement e = n.firstChild().toElement(); !e.isNull(); e = e.nextSibling().toElement())
+ if (e.tagName() == QString::fromLatin1("day")) {
+ elemText = e.text().lower();
+ if (elemText == QString::fromLatin1("monday"))
+ day = Monday;
+ else if (elemText == QString::fromLatin1("tuesday"))
+ day = Tuesday;
+ else if (elemText == QString::fromLatin1("wednesday"))
+ day = Wednesday;
+ else if (elemText == QString::fromLatin1("thursday"))
+ day = Thursday;
+ else if (elemText == QString::fromLatin1("friday"))
+ day = Friday;
+ else if (elemText == QString::fromLatin1("saturday"))
+ day = Saturday;
+ else if (elemText == QString::fromLatin1("sunday"))
+ day = Sunday;
+ else
+ day = UndefinedDay;
+ if (day != UndefinedDay)
+ d->skipDays.append(day);
+ }
+ }
+}
+
+Document::~Document()
+{
+ if (d->deref())
+ delete d;
+}
+
+bool Document::isValid() const
+{
+ return d->valid;
+}
+
+Version Document::version() const
+{
+ return d->version;
+}
+
+QString Document::verbVersion() const
+{
+ switch (d->version) {
+ case v0_90: return QString::fromLatin1("0.90");
+ case v0_91: return QString::fromLatin1("0.91");
+ case v0_92: return QString::fromLatin1("0.92");
+ case v0_93: return QString::fromLatin1("0.93");
+ case v0_94: return QString::fromLatin1("0.94");
+ case v1_0: return QString::fromLatin1("1.0");
+ case v2_0: return QString::fromLatin1("2.0");
+ case vAtom_0_3: return QString::fromLatin1("0.3");
+ case vAtom_0_2: return QString::fromLatin1("0.2");
+ case vAtom_0_1: return QString::fromLatin1("0.1");
+ }
+ return QString::null;
+}
+
+QString Document::title() const
+{
+ return d->title;
+}
+
+QString Document::description() const
+{
+ return d->description;
+}
+
+const KURL &Document::link() const
+{
+ return d->link;
+}
+
+Image *Document::image()
+{
+ return d->image;
+}
+
+const Image *Document::image() const
+{
+ return d->image;
+}
+
+TextInput *Document::textInput()
+{
+ return d->textInput;
+}
+
+const TextInput *Document::textInput() const
+{
+ return d->textInput;
+}
+
+const Article::List &Document::articles() const
+{
+ return d->articles;
+}
+
+Language Document::language() const
+{
+ return d->language;
+}
+
+QString Document::copyright() const
+{
+ return d->copyright;
+}
+
+const QDateTime &Document::pubDate() const
+{
+ return d->pubDate;
+}
+
+const QDateTime &Document::lastBuildDate() const
+{
+ return d->lastBuildDate;
+}
+
+QString Document::rating() const
+{
+ return d->rating;
+}
+
+const KURL &Document::docs() const
+{
+ return d->docs;
+}
+
+QString Document::managingEditor() const
+{
+ return d->managingEditor;
+}
+
+QString Document::webMaster() const
+{
+ return d->webMaster;
+}
+
+const HourList &Document::skipHours() const
+{
+ return d->skipHours;
+}
+
+const DayList &Document::skipDays() const
+{
+ return d->skipDays;
+}
+
+int Document::ttl() const
+{
+ return d->ttl;
+}
+
+Document &Document::operator=(const Document &other)
+{
+ if (this != &other) {
+ other.d->ref();
+ if (d && d->deref())
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+// vim:noet:ts=4
diff --git a/plugins/rssfeed/rss/document.h b/plugins/rssfeed/rss/document.h
new file mode 100644
index 0000000..1ead634
--- /dev/null
+++ b/plugins/rssfeed/rss/document.h
@@ -0,0 +1,237 @@
+/*
+ * document.h
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_DOCUMENT_H
+#define LIBRSS_DOCUMENT_H
+
+#include "article.h"
+#include "global.h"
+
+class QDateTime;
+class QDomDocument;
+
+namespace RSS
+{
+ class Image;
+ class TextInput;
+
+ /**
+ * Represents a RSS document and provides all the features and properties
+ * as stored in it. You usually don't need to instantiate this one yourself
+ * but rather use Loader::loadFrom() to produce a Document object.
+ * @see Loader::loadForm()
+ */
+ class Document
+ {
+ public:
+ /**
+ * Default constructor.
+ */
+ Document();
+
+ /**
+ * Copy constructor.
+ * @param other The Document object to copy.
+ */
+ Document(const Document &other);
+
+ /**
+ * Constructs a Document from a piece of XML markup.
+ */
+ Document(const QDomDocument &doc);
+
+ /**
+ * Assignment operator.
+ * @param other The Document object to clone.
+ * @return A reference to the cloned Document object.
+ */
+ Document &operator=(const Document &other);
+
+ /**
+ * Destructor.
+ */
+ ~Document();
+
+ /**
+ * @return If document is valid
+ */
+ bool isValid() const;
+
+ /**
+ * @return The version of this document (one of the values of the
+ * enum RSS::Version). This value can be used to determine which
+ * features this RSS document provides.
+ * @see verbVersion()
+ */
+ Version version() const;
+
+ /**
+ * Convenience method. Differs from version() only in how the result
+ * is returned.
+ * @return A QString representing the verbose version of the
+ * document.
+ * @see version()
+ */
+ QString verbVersion() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The title of the RSS document, or QString::null if no
+ * title was available. This is often the name of the news source
+ * from which the RSS document was retrieved.
+ */
+ QString title() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The description of the RSS document, or QString::null
+ * if no description was available. This is usually a short slogan
+ * or description of the news source from which the RSS document
+ * was retrieved.
+ */
+ QString description() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A link pointing to some website, or an empty KURL if no
+ * link was available. This URL mostly points to the homepage of
+ * the news site from which the RSS document was retrieved.
+ * Note that the RSS 0.91 Specification dictates that URLs not
+ * starting with "http://" or "ftp://" are considered invalid.
+ */
+ const KURL &link() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return An Image object as stored in the RSS document, or a
+ * null pointer if there was no image available.
+ * @see Image
+ */
+ Image *image();
+
+ /**
+ * A version of the method above, with stricter const-ness.
+ */
+ const Image *image() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A TextInput object as stored in the RSS document, or a
+ * null pointer if there was no text input available.
+ * @see TextInput
+ */
+ TextInput *textInput();
+
+ /**
+ * A version of the method above, with stricter const-ness.
+ */
+ const TextInput *textInput() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A list of Article objects as stored in the RSS document,
+ * or a null pointer if there were no articles available. Every RSS
+ * DTD requires that there is at least one article defined, so a
+ * null pointer indicates an invalid RSS file!
+ * @see Article
+ */
+ const Article::List &articles() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The language used in the RSS document (for the article
+ * headlines etc.). This was originally introduced to assist with
+ * determining the correct page encoding but acts as a solely
+ * optional information in this library since you don't have to care
+ * about the encoding as Unicode is used in the whole library.
+ * @see RSS::Language
+ */
+ Language language() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A copyright of the information contained in the RSS
+ * document, or QString::null if no copyright is available.
+ */
+ QString copyright() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The date when the RSS document was published.
+ */
+ const QDateTime &pubDate() const;
+
+ /**
+ * RSS 0.91 and upwards.
+ * @return The last time the channel was modified.
+ */
+ const QDateTime &lastBuildDate() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A <a href="http://www.w3.org/PICS/#Specs">PICS</a>
+ * rating for this page.
+ */
+ QString rating() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return This tag should contain either a URL that references a
+ * description of the channel, or a pointer to the documentation
+ * for the format used in the RSS file.
+ */
+ const KURL &docs() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The email address of the managing editor of the site,
+ * the person to contact for editorial inquiries. The suggested
+ * format for email addresses in RSS documents is
+ * bull@mancuso.com (Bull Mancuso).
+ * @see webMaster()
+ */
+ QString managingEditor() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The email address of the webmaster for the site, the
+ * person to contact if there are technical problems with the
+ * channel, or QString::null if this information isn't available.
+ * @see managingEditor()
+ */
+ QString webMaster() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A list of hours indicating the hours in the day, GMT,
+ * when the channel is unlikely to be updated. If this item is
+ * omitted, the channel is assumed to be updated hourly. Each
+ * hour should be an integer value between 0 and 23.
+ * @see skipDays()
+ */
+ const HourList &skipHours() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A list of <day>s of the week, in English, indicating
+ * the days of the week when the RSS document will not be updated.
+ * @see skipHours(), DayList, Day
+ */
+ const DayList &skipDays() const;
+ int ttl() const;
+
+ private:
+ struct Private;
+ Private *d;
+ };
+}
+
+#endif // LIBRSS_DOCUMENT_H
+// vim: noet:ts=4
diff --git a/plugins/rssfeed/rss/global.h b/plugins/rssfeed/rss/global.h
new file mode 100644
index 0000000..3a954e6
--- /dev/null
+++ b/plugins/rssfeed/rss/global.h
@@ -0,0 +1,145 @@
+/*
+ * global.h
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_GLOBAL_H
+#define LIBRSS_GLOBAL_H
+
+template <class>
+class QValueList;
+
+namespace RSS
+{
+ /**
+ * Versions currently supported by this library. This enumeration is
+ * subject to be extended in the future and used by Document::version() to
+ * provide an interface to the client using which he can find out what
+ * version the loaded RSS file actually is.
+ */
+ enum Version {
+ v0_90, /// RSS v0.90
+ v0_91, /// RSS v0.91
+ v0_92, /// RSS v0.92
+ v0_93, /// RSS v0.93
+ v0_94, /// RSS v0.94
+ v1_0, /// RSS v1.0
+ v2_0, /// RSS v2.0
+ vAtom_0_1, /// Atom v0.1
+ vAtom_0_2, /// Atom v0.2
+ vAtom_0_3 /// Atom v0.3
+ };
+
+ /**
+ * Possible status values returned by the signal
+ * Loader::loadingComplete().
+ */
+ enum Status {
+ Success, /**
+ * Nothing went wrong so far, but you still have to check
+ * what values are returned by the classes since it's not
+ * guaranteed that the retrieved RSS markup actually
+ * complies to one of the RSS DTDs.*/
+ Aborted, /** the loader was aborted manually
+ */
+ RetrieveError, /**
+ * Something went wrong while retrieving the RSS data,
+ * this could be a problem while resolving the host name
+ * (assuming the source file loader was used) or a
+ * problem with the program to be executed (in case the
+ * program loader was used.).*/
+ ParseError /**
+ * The overall format of the RSS markup wasn't XML
+ * conform. This only indicates that the data wasn't
+ * valid (for example, if the data returned by a
+ * DataRetriever isn't well-formed XML).
+ * @see DataRetriever */
+ };
+
+ /**
+ * Possible languages which are returned by Document::language().
+ */
+ enum Language {
+ UndefinedLanguage, /** Unknown / undefined language */
+
+ af, /** Afrikaans */ sq, /** Albanian */
+ eu, /** Basque */ be, /** Belarusian */
+ bg, /** Bulgarian */ ca, /** Catalan */
+ zh_cn, /** Chinese (Simplified) */ zh_tw, /** Chinese (Traditional */
+ hr, /** Croatian */ cs, /** Czech */
+ da, /** Danish */ nl, /** Dutch */
+ nl_be, /** Dutch (Belgium) */ nl_nl, /** Dutch (Netherlands) */
+ en, /** English */ en_au, /** English (Australia) */
+ en_bz, /** English (Belize) */ en_ca, /** English (Canada) */
+ en_ie, /** English (Ireland) */ en_jm, /** English (Jamaica) */
+ en_nz, /** English (New Zealand) */ en_ph, /** English (Phillipines) */
+ en_za, /** English (South Africa) */ en_tt, /** English (Trinidad) */
+ en_gb, /** English (Great Britain) */en_us, /** English (United States) */
+ en_zw, /** English (Zimbabwe) */ fo, /** Faeroese */
+ fi, /** Finnish */ fr, /** French */
+ fr_be, /** French (Belgium) */ fr_ca, /** French (Canada) */
+ fr_fr, /** French (France) */ fr_lu, /** French (Luxembourg) */
+ fr_mc, /** French (Monaco) */ fr_ch, /** French (Switzerland) */
+ gl, /** Galician */ gd, /** Gaelic */
+ de, /** German */ de_at, /** German (Austria) */
+ de_de, /** German (Germany) */ de_li, /** German (Liechtenstein) */
+ de_lu, /** German (Luxembourg) */ de_ch, /** German (Switzerland) */
+ el, /** Greek */ hu, /** Hungarian */
+ is, /** Icelandic */ id, /** Indonesian */
+ ga, /** Irish */ it, /** Italian */
+ it_it, /** Italian (Italy) */ it_ch, /** Italian (Switzerland) */
+ ja, /** Japanese */ ko, /** Korean */
+ mk, /** Macedonian */ no, /** Norwegian */
+ pl, /** Polish */ pt, /** Portuguese */
+ pt_br, /** Portuguese (Brazil) */ pt_pt, /** Portuguese (Portugal) */
+ ro, /** Romanian */ ro_mo, /** Romanian (Moldova) */
+ ro_ro, /** Romanian (Romania) */ ru, /** Russian */
+ ru_mo, /** Russian (Moldova) */ ru_ru, /** Russian (Russia) */
+ sr, /** Serbian */ sk, /** Slovak */
+ sl, /** Slovenian */ es, /** Spanish */
+ es_ar, /** Spanish (Argentina) */ es_bo, /** Spanish (Bolivia) */
+ es_cl, /** Spanish (Chile) */ es_co, /** Spanish (Colombia) */
+ es_cr, /** Spanish (Costa Rica) */ es_do, /** Spanish (Dominican Rep.) */
+ es_ec, /** Spanish (Ecuador) */ es_sv, /** Spanish (El Salvador) */
+ es_gt, /** Spanish (Guatemala) */ es_hn, /** Spanish (Honduras) */
+ es_mx, /** Spanish (Mexico) */ es_ni, /** Spanish (Nicaragua) */
+ es_pa, /** Spanish (Panama) */ es_py, /** Spanish (Paraguay) */
+ es_pe, /** Spanish (Peru) */ es_pr, /** Spanish (Puerto Rico) */
+ es_es, /** Spanish (Spain) */ es_uy, /** Spanish (Uruguay) */
+ es_ve, /** Spanish (Venezuela) */ sv, /** Swedish */
+ sv_fi, /** Swedish (Finland) */ sv_se, /** Swedish (Sweden) */
+ tr, /** Turkish */ uk /** Ukranian */
+ };
+
+ /**
+ * Possible values contained in a DayList.
+ */
+ enum Day {
+ UndefinedDay,
+ Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
+ };
+
+ enum Format {
+ UnknownFormat,
+ AtomFeed,
+ RSSFeed
+ };
+
+ /**
+ * This type is used by Document::skipDays().
+ */
+ typedef QValueList<Day> DayList;
+
+ /**
+ * This type is used by Document::skipHours().
+ */
+ typedef QValueList<unsigned short> HourList;
+}
+
+#endif // LIBRSS_GLOBAL_H
+// vim: noet:ts=4
diff --git a/plugins/rssfeed/rss/image.cpp b/plugins/rssfeed/rss/image.cpp
new file mode 100644
index 0000000..33e1544
--- /dev/null
+++ b/plugins/rssfeed/rss/image.cpp
@@ -0,0 +1,167 @@
+/*
+ * image.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "image.h"
+#include "tools_p.h"
+
+#include <kio/job.h>
+#include <kurl.h>
+
+#include <qbuffer.h>
+#include <qdom.h>
+#include <qpixmap.h>
+
+using namespace RSS;
+
+struct Image::Private : public Shared
+{
+ Private() : height(31), width(88), pixmapBuffer(NULL), job(NULL)
+ { }
+
+ QString title;
+ KURL url;
+ KURL link;
+ QString description;
+ unsigned int height;
+ unsigned int width;
+ QBuffer *pixmapBuffer;
+ KIO::Job *job;
+};
+
+Image::Image() : QObject(), d(new Private)
+{
+}
+
+Image::Image(const Image &other) : QObject(), d(0)
+{
+ *this = other;
+}
+
+Image::Image(const QDomNode &node) : QObject(), d(new Private)
+{
+ QString elemText;
+
+ if (!(elemText = extractNode(node, QString::fromLatin1("title"))).isNull())
+ d->title = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("url"))).isNull())
+ d->url = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("link"))).isNull())
+ d->link = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("description"))).isNull())
+ d->description = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("height"))).isNull())
+ d->height = elemText.toUInt();
+ if (!(elemText = extractNode(node, QString::fromLatin1("width"))).isNull())
+ d->width = elemText.toUInt();
+}
+
+Image::~Image()
+{
+ if (d->deref())
+ {
+ delete d->pixmapBuffer;
+ d->pixmapBuffer=0L;
+ delete d;
+ }
+}
+
+QString Image::title() const
+{
+ return d->title;
+}
+
+const KURL &Image::url() const
+{
+ return d->url;
+}
+
+const KURL &Image::link() const
+{
+ return d->link;
+}
+
+QString Image::description() const
+{
+ return d->description;
+}
+
+unsigned int Image::height() const
+{
+ return d->height;
+}
+
+unsigned int Image::width() const
+{
+ return d->width;
+}
+
+void Image::getPixmap()
+{
+ // Ignore subsequent calls if we didn't finish the previous download.
+ if (d->pixmapBuffer)
+ return;
+
+ d->pixmapBuffer = new QBuffer;
+ d->pixmapBuffer->open(IO_WriteOnly);
+
+ d->job = KIO::get(d->url, false, false);
+ connect(d->job, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ this, SLOT(slotData(KIO::Job *, const QByteArray &)));
+ connect(d->job, SIGNAL(result(KIO::Job *)), this, SLOT(slotResult(KIO::Job *)));
+}
+
+void Image::slotData(KIO::Job *, const QByteArray &data)
+{
+ d->pixmapBuffer->writeBlock(data.data(), data.size());
+}
+
+void Image::slotResult(KIO::Job *job)
+{
+ QPixmap pixmap;
+ if (!job->error())
+ pixmap = QPixmap(d->pixmapBuffer->buffer());
+ emit gotPixmap(pixmap);
+
+ delete d->pixmapBuffer;
+ d->pixmapBuffer = NULL;
+}
+
+void Image::abort()
+{
+ if (d->job)
+ {
+ d->job->kill(true);
+ d->job = NULL;
+ }
+}
+
+Image &Image::operator=(const Image &other)
+{
+ if (this != &other) {
+ other.d->ref();
+ if (d && d->deref())
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+bool Image::operator==(const Image &other) const
+{
+ return d->title == other.title() &&
+ d->url == other.url() &&
+ d->description == other.description() &&
+ d->height == other.height() &&
+ d->width == other.width() &&
+ d->link == other.link();
+}
+
+#include "image.moc"
+// vim:noet:ts=4
diff --git a/plugins/rssfeed/rss/image.h b/plugins/rssfeed/rss/image.h
new file mode 100644
index 0000000..e9e65b1
--- /dev/null
+++ b/plugins/rssfeed/rss/image.h
@@ -0,0 +1,173 @@
+/*
+ * image.h
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_IMAGE_H
+#define LIBRSS_IMAGE_H
+
+#include "global.h"
+
+#include <qobject.h>
+
+class QDomNode;
+
+namespace KIO
+{
+ class Job;
+}
+class KURL;
+
+namespace RSS
+{
+ /**
+ * Represents an image as stored in a RSS file. You don't have to
+ * instantiate one of these yourself, the common way to access instances
+ * is via Document::image().
+ * @see Document::image()
+ */
+ class Image : public QObject
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Default constructor.
+ */
+ Image();
+
+ /**
+ * Copy constructor.
+ * @param other The Image object to copy.
+ */
+ Image(const Image &other);
+
+ /**
+ * Constructs an Image from a piece of RSS markup.
+ * @param node A QDomNode which references the DOM leaf to be used
+ * for constructing the Image.
+ */
+ Image(const QDomNode &node);
+
+ /**
+ * Assignment operator.
+ * @param other The Image object to clone.
+ * @return A reference to the cloned Image object.
+ */
+ Image &operator=(const Image &other);
+
+ /**
+ * Compares two images. Two images are considered identical if
+ * their properties (title, description, link etc.) are identical.
+ * Note that this does not include the actual pixmap data!
+ * @param other The image to compare with.
+ * @return Whether the two images are equal.
+ */
+ bool operator==(const Image &other) const;
+
+ /**
+ * Convenience method. Simply calls !operator==().
+ * @param other The image to compared with.
+ * @return Whether the two images are unequal.
+ */
+ bool operator!=(const Image &other) const { return !operator==(other); }
+
+ /**
+ * Destructor.
+ */
+ virtual ~Image();
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The 'caption' of this image, or QString::null if no
+ * caption is available.
+ */
+ QString title() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The URL pointing to the file containing the graphic
+ * data (GIF, JPEG or PNG format), or an empty KURL if no URL
+ * is available. You can use getPixmap() and gotPixmap() to have
+ * the Image download the pixmap data itself.
+ * Note that the RSS 0.91 Specification dictates that URLs not
+ * starting with "http://" or "ftp://" are considered invalid.
+ */
+ const KURL &url() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A link to some resource, or an empty KURL of no link is
+ * available. Clicking on the image should lead the user to the
+ * resource referenced by this URL.
+ * Note that the RSS 0.91 Specification dictates that URLs not
+ * starting with "http://" or "ftp://" are considered invalid.
+ */
+ const KURL &link() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A description of what this picture shows, or
+ * QString::null if no description is available. Useful for
+ * people who deactivated images but want or need to know what is
+ * shown.
+ */
+ QString description() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The height in pixels as reported by the news site, the
+ * default value is 31 pixels. The RSS 0.91 Specification requires
+ * this value to be between 1 and 400.
+ * '0' if this information isn't available. This is merely provided
+ * for completeness, you should not rely on this value but rather
+ * check what height the QPixmap as returned by gotPixmap()
+ * reports.
+ */
+ unsigned int height() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The width in pixels as reported by the news site, the
+ * default value is 88 pixels. The RSS 0.91 Specification requires
+ * this value to be between 1 and 144.
+ * This is merely provided for completeness, you should not rely
+ * on this value but rather check what width the QPixmap as
+ * returned by gotPixmap() reports.
+ */
+ unsigned int width() const;
+
+ /**
+ * Makes the image download the image data as referenced by the
+ * URL returned by url(). You have to connect to the signal
+ * gotPixmap() first and then call getPixmap().
+ */
+ void getPixmap();
+ void abort();
+
+ signals:
+ /**
+ * Emitted when this Image is done downloading the actual graphics
+ * data as referenced by the URL returned by url(). You can trigger
+ * this download by calling getPixmap().
+ * @param pixmap The pixmap as constructed from the data referenced
+ * by the URL returned by link().
+ */
+ void gotPixmap(const QPixmap &pixmap);
+
+ private slots:
+ void slotData(KIO::Job *job, const QByteArray &data);
+ void slotResult(KIO::Job *job);
+
+ private:
+ struct Private;
+ Private *d;
+ };
+}
+
+#endif // LIBRSS_IMAGE_H
+// vim: noet:ts=4
diff --git a/plugins/rssfeed/rss/librss.doxyfile b/plugins/rssfeed/rss/librss.doxyfile
new file mode 100644
index 0000000..c81ac16
--- /dev/null
+++ b/plugins/rssfeed/rss/librss.doxyfile
@@ -0,0 +1,921 @@
+# Doxyfile 1.2.14
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = librss
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 0.1
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French,
+# German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish,
+# Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish.
+
+OUTPUT_LANGUAGE = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these class will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH =
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower case letters. If set to YES upper case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consist of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse.
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+,
+# or Internet explorer 4.0+). Note that for large projects the tree generation
+# can take a very long time. In such cases it is better to disable this feature.
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimised for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assigments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_XML = NO
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line and do not end with a semicolon. Such function macros are typically
+# used for boiler-plate code, and will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are gif, jpg, and png
+# If left blank gif will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermedate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
+
+# The CGI_NAME tag should be the name of the CGI script that
+# starts the search engine (doxysearch) with the correct parameters.
+# A script with this name will be generated by doxygen.
+
+CGI_NAME = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the
+# cgi binaries are located. See the documentation of your http daemon for
+# details.
+
+CGI_URL =
+
+# The DOC_URL tag should be the absolute URL to the directory where the
+# documentation is located. If left blank the absolute path to the
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL =
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the
+# documentation is located. If left blank the directory on the local machine
+# will be used.
+
+DOC_ABSPATH =
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
+# is installed.
+
+BIN_ABSPATH = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to
+# documentation generated for other projects. This allows doxysearch to search
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS =
diff --git a/plugins/rssfeed/rss/librss.h b/plugins/rssfeed/rss/librss.h
new file mode 100644
index 0000000..c9637d0
--- /dev/null
+++ b/plugins/rssfeed/rss/librss.h
@@ -0,0 +1,22 @@
+/*
+ * librss.h
+ *
+ * Copyright (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_LIBRSS_H
+#define LIBRSS_LIBRSS_H
+
+#include "article.h"
+#include "document.h"
+#include "global.h"
+#include "image.h"
+#include "loader.h"
+#include "textinput.h"
+
+#endif // LIBRSS_LIBRSS_H
+// vim: noet:ts=4
diff --git a/plugins/rssfeed/rss/loader.cpp b/plugins/rssfeed/rss/loader.cpp
new file mode 100644
index 0000000..9dfb50a
--- /dev/null
+++ b/plugins/rssfeed/rss/loader.cpp
@@ -0,0 +1,425 @@
+/*
+ * loader.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "loader.h"
+#include "document.h"
+
+#include <kio/job.h>
+#include <kprocess.h>
+#include <kurl.h>
+#include <kdebug.h>
+
+#include <qdom.h>
+#include <qbuffer.h>
+#include <qregexp.h>
+#include <qstringlist.h>
+#include <qtimer.h>
+
+using namespace RSS;
+
+DataRetriever::DataRetriever()
+{
+}
+
+DataRetriever::~DataRetriever()
+{
+}
+
+struct FileRetriever::Private
+{
+ Private()
+ : buffer(NULL),
+ lastError(0), job(NULL)
+ {
+ }
+
+ ~Private()
+ {
+ delete buffer;
+ }
+
+ QBuffer *buffer;
+ int lastError;
+ KIO::Job *job;
+};
+
+FileRetriever::FileRetriever()
+ : d(new Private)
+{
+}
+
+FileRetriever::~FileRetriever()
+{
+ delete d;
+}
+
+bool FileRetriever::m_useCache = true;
+
+void FileRetriever::setUseCache(bool enabled)
+{
+ m_useCache = enabled;
+}
+
+void FileRetriever::retrieveData(const KURL &url)
+{
+ if (d->buffer)
+ return;
+
+ d->buffer = new QBuffer;
+ d->buffer->open(IO_WriteOnly);
+
+ KURL u=url;
+
+ if (u.protocol()=="feed")
+ u.setProtocol("http");
+
+ d->job = KIO::get(u, !m_useCache, false);
+
+
+ QTimer::singleShot(1000*90, this, SLOT(slotTimeout()));
+
+ connect(d->job, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ SLOT(slotData(KIO::Job *, const QByteArray &)));
+ connect(d->job, SIGNAL(result(KIO::Job *)), SLOT(slotResult(KIO::Job *)));
+ connect(d->job, SIGNAL(permanentRedirection(KIO::Job *, const KURL &, const KURL &)),
+ SLOT(slotPermanentRedirection(KIO::Job *, const KURL &, const KURL &)));
+}
+
+void FileRetriever::slotTimeout()
+{
+ abort();
+
+ delete d->buffer;
+ d->buffer = NULL;
+
+ d->lastError = KIO::ERR_SERVER_TIMEOUT;
+
+ emit dataRetrieved(QByteArray(), false);
+}
+
+int FileRetriever::errorCode() const
+{
+ return d->lastError;
+}
+
+void FileRetriever::slotData(KIO::Job *, const QByteArray &data)
+{
+ d->buffer->writeBlock(data.data(), data.size());
+}
+
+void FileRetriever::slotResult(KIO::Job *job)
+{
+ QByteArray data = d->buffer->buffer();
+ data.detach();
+
+ delete d->buffer;
+ d->buffer = NULL;
+
+ d->lastError = job->error();
+ emit dataRetrieved(data, d->lastError == 0);
+}
+
+void FileRetriever::slotPermanentRedirection(KIO::Job *, const KURL &, const KURL &newUrl)
+{
+ emit permanentRedirection(newUrl);
+}
+
+void FileRetriever::abort()
+{
+ if (d->job)
+ {
+ d->job->kill(true);
+ d->job = NULL;
+ }
+}
+
+struct OutputRetriever::Private
+{
+ Private() : process(NULL),
+ buffer(NULL),
+ lastError(0)
+ {
+ }
+
+ ~Private()
+ {
+ delete process;
+ delete buffer;
+ }
+
+ KShellProcess *process;
+ QBuffer *buffer;
+ int lastError;
+};
+
+OutputRetriever::OutputRetriever() :
+ d(new Private)
+{
+}
+
+OutputRetriever::~OutputRetriever()
+{
+ delete d;
+}
+
+void OutputRetriever::retrieveData(const KURL &url)
+{
+ // Ignore subsequent calls if we didn't finish the previous job yet.
+ if (d->buffer || d->process)
+ return;
+
+ d->buffer = new QBuffer;
+ d->buffer->open(IO_WriteOnly);
+
+ d->process = new KShellProcess();
+ connect(d->process, SIGNAL(processExited(KProcess *)),
+ SLOT(slotExited(KProcess *)));
+ connect(d->process, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ SLOT(slotOutput(KProcess *, char *, int)));
+ *d->process << url.path();
+ d->process->start(KProcess::NotifyOnExit, KProcess::Stdout);
+}
+
+int OutputRetriever::errorCode() const
+{
+ return d->lastError;
+}
+
+void OutputRetriever::slotOutput(KProcess *, char *data, int length)
+{
+ d->buffer->writeBlock(data, length);
+}
+
+void OutputRetriever::slotExited(KProcess *p)
+{
+ if (!p->normalExit())
+ d->lastError = p->exitStatus();
+
+ QByteArray data = d->buffer->buffer();
+ data.detach();
+
+ delete d->buffer;
+ d->buffer = NULL;
+
+ delete d->process;
+ d->process = NULL;
+
+ emit dataRetrieved(data, p->normalExit() && p->exitStatus() == 0);
+}
+
+struct Loader::Private
+{
+ Private() : retriever(NULL),
+ lastError(0)
+ {
+ }
+
+ ~Private()
+ {
+ delete retriever;
+ }
+
+ DataRetriever *retriever;
+ int lastError;
+ KURL discoveredFeedURL;
+ KURL url;
+};
+
+Loader *Loader::create()
+{
+ return new Loader;
+}
+
+Loader *Loader::create(QObject *object, const char *slot)
+{
+ Loader *loader = create();
+ connect(loader, SIGNAL(loadingComplete(Loader *, Document, Status)),
+ object, slot);
+ return loader;
+}
+
+Loader::Loader() : d(new Private)
+{
+}
+
+Loader::~Loader()
+{
+ delete d;
+}
+
+void Loader::loadFrom(const KURL &url, DataRetriever *retriever)
+{
+ if (d->retriever != NULL)
+ return;
+
+ d->url=url;
+ d->retriever = retriever;
+
+ connect(d->retriever, SIGNAL(dataRetrieved(const QByteArray &, bool)),
+ this, SLOT(slotRetrieverDone(const QByteArray &, bool)));
+
+ d->retriever->retrieveData(url);
+}
+
+int Loader::errorCode() const
+{
+ return d->lastError;
+}
+
+void Loader::abort()
+{
+ if (d && d->retriever)
+ {
+ d->retriever->abort();
+ delete d->retriever;
+ d->retriever=NULL;
+ }
+ emit loadingComplete(this, QDomDocument(), Aborted);
+ delete this;
+}
+
+const KURL &Loader::discoveredFeedURL() const
+{
+ return d->discoveredFeedURL;
+}
+
+#include <kdebug.h>
+
+void Loader::slotRetrieverDone(const QByteArray &data, bool success)
+{
+ d->lastError = d->retriever->errorCode();
+
+ delete d->retriever;
+ d->retriever = NULL;
+
+ Document rssDoc;
+ Status status = Success;
+
+ if (success) {
+ QDomDocument doc;
+
+ /* Some servers insert whitespace before the <?xml...?> declaration.
+ * QDom doesn't tolerate that (and it's right, that's invalid XML),
+ * so we strip that.
+ */
+
+ const char *charData = data.data();
+ int len = data.count();
+
+ while (len && QChar(*charData).isSpace()) {
+ --len;
+ ++charData;
+ }
+
+ if ( len > 3 && QChar(*charData) == QChar(0357) ) { // 0357 0273 0277
+ len -= 3;
+ charData += 3;
+ }
+ QByteArray tmpData;
+ tmpData.setRawData(charData, len);
+
+ if (doc.setContent(tmpData))
+ {
+ rssDoc = Document(doc);
+ if (!rssDoc.isValid())
+ {
+ discoverFeeds(tmpData);
+ status = ParseError;
+ }
+ }
+ else
+ {
+ discoverFeeds(tmpData);
+ status = ParseError;
+ }
+
+ tmpData.resetRawData(charData, len);
+ } else
+ status = RetrieveError;
+
+ emit loadingComplete(this, rssDoc, status);
+
+ delete this;
+}
+
+void Loader::discoverFeeds(const QByteArray &data)
+{
+ QString str = QString(data).simplifyWhiteSpace();
+ QString s2;
+ //QTextStream ts( &str, IO_WriteOnly );
+ //ts << data.data();
+
+ // "<[\\s]link[^>]*rel[\\s]=[\\s]\\\"[\\s]alternate[\\s]\\\"[^>]*>"
+ // "type[\\s]=[\\s]\\\"application/rss+xml\\\""
+ // "href[\\s]=[\\s]\\\"application/rss+xml\\\""
+ QRegExp rx( "(?:REL)[^=]*=[^sAa]*(?:service.feed|ALTERNATE)[\\s]*[^s][^s](?:[^>]*)(?:HREF)[^=]*=[^A-Z0-9-_~,./$]*([^'\">\\s]*)", false);
+ if (rx.search(str)!=-1)
+ s2=rx.cap(1);
+ else{
+ // does not support Atom/RSS autodiscovery.. try finding feeds by brute force....
+ int pos=0;
+ QStringList feeds;
+ QString host=d->url.host();
+ rx.setPattern("(?:<A )[^H]*(?:HREF)[^=]*=[^A-Z0-9-_~,./]*([^'\">\\s]*)");
+ while ( pos >= 0 ) {
+ pos = rx.search( str, pos );
+ s2=rx.cap(1);
+ if (s2.endsWith(".rdf")|s2.endsWith(".rss")|s2.endsWith(".xml"))
+ feeds.append(s2);
+ if ( pos >= 0 ) {
+ pos += rx.matchedLength();
+ }
+ }
+
+ s2=feeds.first();
+ KURL testURL;
+ // loop through, prefer feeds on same host
+ for ( QStringList::Iterator it = feeds.begin(); it != feeds.end(); ++it ) {
+ testURL=*it;
+ if (testURL.host()==host)
+ {
+ s2=*it;
+ break;
+ }
+ }
+ }
+
+ if (s2.isNull()) {
+ kdDebug() << "No feed found for a site" << endl;
+ return;
+ }
+
+ if (KURL::isRelativeURL(s2))
+ {
+ if (s2.startsWith("//"))
+ {
+ s2=s2.prepend(d->url.protocol()+":");
+ d->discoveredFeedURL=s2;
+ }
+ else if (s2.startsWith("/"))
+ {
+ d->discoveredFeedURL=d->url;
+ d->discoveredFeedURL.setPath(s2);
+ }
+ else
+ {
+ d->discoveredFeedURL=d->url;
+ d->discoveredFeedURL.addPath(s2);
+ }
+ d->discoveredFeedURL.cleanPath();
+ }
+ else
+ d->discoveredFeedURL=s2;
+
+ d->discoveredFeedURL.cleanPath();
+}
+
+#include "loader.moc"
+// vim:noet:ts=4
diff --git a/plugins/rssfeed/rss/loader.h b/plugins/rssfeed/rss/loader.h
new file mode 100644
index 0000000..fb06634
--- /dev/null
+++ b/plugins/rssfeed/rss/loader.h
@@ -0,0 +1,339 @@
+/*
+ * loader.h
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_LOADER_H
+#define LIBRSS_LOADER_H
+
+#include "global.h"
+
+class KURL;
+
+#include <qobject.h>
+
+namespace KIO
+{
+ class Job;
+}
+class KProcess;
+
+namespace RSS
+{
+ class Document;
+
+ /**
+ * Abstract baseclass for all data retriever classes. Subclass this to add
+ * a new retrieval algorithm which can then be plugged into the RSS loader.
+ * @see Loader, FileRetriever, OutputRetriever
+ */
+ class DataRetriever : public QObject
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Default constructor.
+ */
+ DataRetriever();
+
+ /**
+ * Destructor.
+ */
+ virtual ~DataRetriever();
+
+ /**
+ * Retrieve data from the given URL. This method is supposed to get
+ * reimplemented by subclasses. It will be called by the Loader
+ * class in case it needs to retrieve the data.
+ * @see Loader::loadFrom()
+ */
+ virtual void retrieveData(const KURL &url) = 0;
+
+ /**
+ * @return An error code which might give a more precise information
+ * about what went wrong in case the 'success' flag returned with
+ * the dataRetrieved() signal was 'false'. Note that the meaning of
+ * the returned integer depends on the actual data retriever.
+ */
+ virtual int errorCode() const = 0;
+
+ virtual void abort() = 0;
+ signals:
+ /**
+ * Emit this signal to tell the Loader class that the retrieval
+ * process was finished.
+ * @param data Should contain the retrieved data and will get
+ * parsed by the Loader class.
+ * @param success Indicates whether there were any problems during
+ * the retrieval process. Pass 'true' to indicate that everything
+ * went seamlessy, 'false' to tell the Loader that something went
+ * wrong and that the data parameter might contain no or invalid
+ * data.
+ */
+ void dataRetrieved(const QByteArray &data, bool success);
+
+ private:
+ DataRetriever(const DataRetriever &other);
+ DataRetriever &operator=(const DataRetriever &other);
+ };
+
+ /**
+ * Implements a file retriever, to be used with Loader::loadFrom().
+ * @see DataRetriever, Loader::loadFrom()
+ */
+ class FileRetriever : public DataRetriever
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Default constructor.
+ */
+ FileRetriever();
+
+ /**
+ * Destructor.
+ */
+ virtual ~FileRetriever();
+
+ /**
+ * Downloads the file referenced by the given URL and passes it's
+ * contents on to the Loader.
+ * @param url An URL referencing a file which is assumed to
+ * reference valid XML.
+ * @see Loader::loadFrom()
+ */
+ virtual void retrieveData(const KURL &url);
+
+ /**
+ * @return The error code for the last process of retrieving data.
+ * The returned numbers correspond directly to the error codes
+ * <a href="http://developer.kde.org/documentation/library/cvs-api/classref/kio/KIO.html#Error">as
+ * defined by KIO</a>.
+ */
+ virtual int errorCode() const;
+
+ virtual void abort();
+
+ static void setUseCache(bool enabled);
+
+ signals:
+ /**
+ * Signals a permanent redirection. The redirection itself is
+ * handled internally, so you don't need to call Loader::loadFrom()
+ * with the new URL. This signal is useful in case you want to
+ * notify the user, or adjust a database entry.
+ * @see Loader::loadFrom()
+ */
+ void permanentRedirection(const KURL &url);
+
+ protected slots:
+ void slotTimeout();
+
+ private slots:
+ void slotData(KIO::Job *job, const QByteArray &data);
+ void slotResult(KIO::Job *job);
+ void slotPermanentRedirection(KIO::Job *job, const KURL &fromUrl,
+ const KURL &toUrl);
+
+ private:
+ static bool m_useCache;
+
+ FileRetriever(const FileRetriever &other);
+ FileRetriever &operator=(const FileRetriever &other);
+
+ struct Private;
+ Private *d;
+ };
+
+ /**
+ * Implements a data retriever which executes a program and stores returned
+ * by the program on stdout. To be used with Loader::loadFrom().
+ * @see DataRetriever, Loader::loadFrom()
+ */
+ class OutputRetriever : public DataRetriever
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Default constructor.
+ */
+ OutputRetriever();
+
+ /**
+ * Destructor.
+ */
+ virtual ~OutputRetriever();
+
+ /**
+ * Executes the program referenced by the given URL and retrieves
+ * the data which the program prints to stdout.
+ * @param url An URL which is supposed to reference an executable
+ * file.
+ * @see Loader::loadFrom()
+ */
+ virtual void retrieveData(const KURL &url);
+
+ /**
+ * @return The error code for the last process of retrieving data.
+ * 0 is returned in case there was no error, otherwise an error
+ * code which depends on the particular program which was run is
+ * returned.
+ */
+ virtual int errorCode() const;
+
+ virtual void abort() {}
+
+ private slots:
+ void slotOutput(KProcess *process, char *data, int length);
+ void slotExited(KProcess *process);
+
+ private:
+ OutputRetriever(const OutputRetriever &other);
+ OutputRetriever &operator=(const OutputRetriever &other);
+
+ struct Private;
+ Private *d;
+ };
+
+ /**
+ * This class is the preferred way of loading RSS files. Usage is very
+ * straightforward:
+ *
+ * \code
+ * Loader *loader = Loader::create();
+ * connect(loader, SIGNAL(loadingComplete(Loader *, Document, Status)),
+ * this, SLOT(slotLoadingComplete(Loader *, Document, Status)));
+ * loader->loadFrom("http://www.blah.org/foobar.rdf", new FileRetriever);
+ * \endcode
+ *
+ * This creates a Loader object, connects it's loadingComplete() signal to
+ * your custom slot and then makes it load the file
+ * 'http://www.blah.org/foobar.rdf' using the FileRetriever. You could've
+ * done something like this as well:
+ *
+ * \code
+ * // create the Loader, connect it's signal...
+ * loader->loadFrom("/home/myself/some-script.py", new OutputRetriever);
+ * \endcode
+ *
+ * That'd make the Loader use another algorithm for retrieving the RSS data;
+ * 'OutputRetriever' will make it execute the script
+ * '/home/myself/some-script.py' and assume whatever that script prints to
+ * stdout is RSS markup. This is e.g. handy for conversion scripts, which
+ * download a HTML file and convert it's contents into RSS markup.
+ *
+ * No matter what kind of retrieval algorithm you employ, your
+ * 'slotLoadingComplete' method might look like this:
+ *
+ * \code
+ * void MyClass::slotLoadingComplete(Loader *loader, Document doc, Status status)
+ * {
+ * // Note that Loader::~Loader() is private, so you cannot delete Loader instances.
+ * // You don't need to do that anyway since Loader instances delete themselves.
+ *
+ * if (status != RSS::Success)
+ * return;
+ *
+ * QString title = doc.title();
+ * // do whatever you want with the information.
+ * }
+ * \endcode
+ *
+ * \note You have to create a copy of the passed Document instance in
+ * case you want/need to use it after the slot attached to the
+ * loadingComplete signal goes out of scope. This is e.g. the case if you
+ * intend to call getPixmap() on Document::image()!
+ */
+ class Loader : public QObject
+ {
+ Q_OBJECT
+ friend class someClassWhichDoesNotExist;
+ public:
+ /**
+ * Constructs a Loader instance. This is pretty much what the
+ * default constructor would do, except that it ensures that all
+ * Loader instances have been allocated on the heap (this is
+ * required so that Loader's can delete themselves safely after they
+ * emitted the loadingComplete() signal.).
+ * @return A pointer to a new Loader instance.
+ */
+ static Loader *create();
+
+ /**
+ * Convenience method. Does the same as the above method except that
+ * it also does the job of connecting the loadingComplete() signal
+ * to the given slot for you.
+ * @param object A QObject which features the specified slot
+ * @param slot Which slot to connect to.
+ */
+ static Loader *create(QObject *object, const char *slot);
+
+ /**
+ * Loads the RSS file referenced by the given URL using the
+ * specified retrieval algorithm. Make sure that you connected
+ * to the loadingComplete() signal before calling this method so
+ * that you're guaranteed to get notified when the loading finished.
+ * \note A Loader object cannot load from multiple URLs simultaneously;
+ * consequently, subsequent calls to loadFrom will be discarded
+ * silently, only the first loadFrom request will be executed.
+ * @param url An URL referencing the input file.
+ * @param retriever A subclass of DataRetriever which implements a
+ * specialized retrieval behaviour. Note that the ownership of the
+ * retriever is transferred to the Loader, i.e. the Loader will
+ * delete it when it doesn't need it anymore.
+ * @see DataRetriever, Loader::loadingComplete()
+ */
+ void loadFrom(const KURL &url, DataRetriever *retriever);
+
+ /**
+ * Retrieves the error code of the last loading process (if any),
+ * as reported by the employed data retrever.
+ */
+ int errorCode() const;
+
+ const KURL &discoveredFeedURL() const;
+
+ void abort();
+
+ signals:
+ /**
+ * This signal gets emitted when the loading process triggered by
+ * calling loadFrom() finished.
+ * @param loader A pointer pointing to the loader object which
+ * emitted this signal; this is handy in case you connect multiple
+ * loaders to a single slot.
+ * @param doc In case status is Success, this parameter holds the
+ * parsed RSS file. In case it's RetrieveError, you should query
+ * loader->errorCode() for the actual error code.
+ * Note that you have to create a copy of the passed Document
+ * instance in case you want/need to use it after the slot attached
+ * to the loadingComplete signal goes out of scope. This is e.g.
+ * the case if you intend to call getPixmap() on Document::image()!
+ * @param status A status byte telling whether there were any problems
+ * while retrieving or parsing the data.
+ * @see Document, Status
+ */
+ void loadingComplete(Loader *loader, Document doc, Status status);
+
+ private slots:
+ void slotRetrieverDone(const QByteArray &data, bool success);
+
+ private:
+ Loader();
+ Loader(const Loader &other);
+ Loader &operator=(const Loader &other);
+ ~Loader();
+ void discoverFeeds(const QByteArray &data);
+
+ struct Private;
+ Private *d;
+ };
+}
+
+#endif // LIBRSS_LOADER_H
+// vim: noet:ts=4
diff --git a/plugins/rssfeed/rss/rss-faq.html b/plugins/rssfeed/rss/rss-faq.html
new file mode 100644
index 0000000..480b19f
--- /dev/null
+++ b/plugins/rssfeed/rss/rss-faq.html
@@ -0,0 +1,396 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+<head>
+<title>RSS Headline Syndication - Frequently Asked Questions for Content Providers</title>
+<meta http-equiv="Content-Type" content="text/html; charset=windows-1252" />
+<meta name="description" content="RSS Headline Syndication - Frequently Asked Questions for Content Providers" />
+<meta name="keywords" content="rss faq, rss, faq, rich site summary, rdf site summary, really simple syndication, headline syndication, syndication" />
+<meta name="robots" content="index,follow" />
+<style type="text/css">
+<!--
+td.content
+{
+font-family: Verdana, Arial, Helvetica, sans-serif;
+color: #000000;
+font-size: 10pt;
+font-weight: normal;
+}
+td.contentbold
+{
+font-family: Verdana, Arial, Helvetica, sans-serif;
+color: #000000;
+font-size: 10pt;
+font-weight: bold;
+}
+td.contentsmall
+{
+font-family: Verdana, Arial, Helvetica, sans-serif;
+color: #000000;
+font-size: 8pt;
+font-weight: normal;
+}
+-->
+</style>
+</head>
+<body bgcolor="#ffffff">
+<p align="right">
+<i>
+ This document was taken from <a href="http://www.purplepages.ie/rss/">http://www.purplepages.ie/rss/</a>.<br/>
+ <tt><a href="mailto:raabe@kde.org">Frerich Raabe</a></tt>
+</i>
+</p>
+<table border="0" width="620" cellpadding="1" cellspacing="0">
+ <tr>
+ <td width="620" class="contentbold" colspan="2">RSS Headline Syndication<br /><br />
+ <a name="top">Frequently Asked Questions for Content Providers</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2">
+ <br /><a href="#whatishs">1. What is headline syndication?</a><br />
+ <a href="#rss">2. What is RSS?</a><br />
+ <a href="#whyrss">3. Why syndicate your headlines with RSS?</a><br />
+ <a href="#howrss">4. How can I create an RSS file?</a><br />
+ <a href="#promoterss">5. How can I promote my RSS file?</a><br />
+ <a href="#morerss">6. Where can I find more information about RSS?</a><br />
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="whatishs">1. What is headline syndication?</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ Websites that publish new content regularly usually provide a list of news headline style links to their latest content. In addition to displaying these headlines on their own websites, it is very common for publishers to make them available for syndication, so that other websites or applications can also include their headlines.<br /><br />
+ Headline syndication does not deal with the full text of articles, it is simply about syndicating an automatically updating list of headlines, with each headline being a link to the item that it refers to on the publishers website.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="rss">2. What is RSS?</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="#top">top</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ RSS is the name given to a simple and well-established XML format used to syndicate headlines. Once a website creates an RSS file they have created a means to allow others to syndicate their headlines.<br /><br />
+ The first version of RSS (RSS 0.9) was released by <a href="http://www.netscape.com/">Netscape</a> in March 1999 as a format for adding news channels to their <a href="http://my.netscape.com/">My.Netscape.Com</a> portal. Then in July 1999 Netscape released RSS 0.91, incorporating most of the features of a format called &lt;scriptingNews&gt;, which was created by <a href="http://www.userland.com/">UserLand</a>. Shortly thereafter Netscape discontinued developing the RSS format, however UserLand persisted and RSS continued to grow in strength. In December 2000, the separate RSS-DEV Working Group released RSS 1.0 and Userland announced RSS 0.92. As of April 2001, Userland is now planning RSS 0.93. Although RSS is not clearly an acronym of anything, different people have called it Rich Site Summary, RDF Site Summary and Really Simple Syndication at different times.<br /><br />
+ The lack of clarity in what RSS stands for or which version is the correct one to use can seem confusing to beginners. However these issues don't need to addressed by a website wanting to create an RSS file. RSS is a very well recognised format, in fact it is often referred to as the most successful XML format to date. Some websites have a preference for one version, others create more than one RSS file and support multiple versions and a recent survey suggests that the first two versions of RSS (0.9 and 0.91) are still by far the most popular.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="#top">top</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="whyrss">3. Why syndicate your headlines with RSS?</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ Syndicating headlines is an excellent and cost-effective way of driving traffic to, and increasing brand awareness of, any website that publishes new content regularly.<br /><br />
+ Once a website produces an RSS file they are enabling others to syndicate their headlines, without any further work on their part.<br /><br />
+ The main benefits of creating an RSS file:<br />&nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">RSS content can be included in customisable online news portals that aggregate RSS headlines like <a href="http://my.userland.com/">My.Userland.Com</a>.
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">Websites that display news headlines can use an RSS file to incorporate another websites headlines into their own.
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">RSS content can be added to personal desktop news reading applications like <a href="http://www.headlineviewer.com/">Headline Viewer</a> or <a href="http://radio.userland.com/">Radio Userland</a>.
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">Email newsletter providers could allow users to subscribe to RSS channels. <a href="http://www.xml.com/">XML.com</a> and <a href="http://www.xmltree.com/">XMLTree.com</a> previously offered such a service called Newsboy.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ One positive side effect of producing an RSS file is that it can also be used by headline aggregation services like <a href="http://www.moreover.com/">Moreover.com</a>, who power news portals, specialist news search engines, business intelligence services or provide newsfeeds to websites. Most such companies use crawler-based technologies to aggregate and do not insist upon content being available in RSS, however they do have some requirements which having an RSS file addresses, sparing the need for any work on the part of a website that already publishes its headlines in RSS.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="#top">top</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="howrss">4. How can I create an RSS file?</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ RSS is a simple XML format and anyone who has experience in a mark-up language like HTML or XML should find it very easy to create and maintain an RSS file by hand.
+ <br /><br />Many websites prefer to generate their RSS file using a programming language, which involves a little more work to begin with but means that maintenance is no longer an issue.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ In this section:
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2">
+ <a href="#specifications">RSS Specifications</a><br />
+ <a href="#validators">RSS Validators</a><br />
+ <a href="#tutorials">RSS Tutorials - The Basics</a><br />
+ <a href="#tutorialsgen">RSS Tutorials - Generating RSS</a><br />
+ <a href="#examples">RSS Examples</a><br />
+ <a href="#tools">RSS Tools &amp; Utilities</a><br />
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="specifications">RSS Specifications:</a><br />&nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" valign="top"><strong>RSS 0.93</strong> (Planning stage, April 2001)<br /><a href="http://backend.userland.com/rss093">http://backend.userland.com/rss093</a> (Userland)
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" valign="top"><strong>RSS 0.92</strong> (December 2000)<br /><a href="http://backend.userland.com/rss092">http://backend.userland.com/rss092</a> (Userland)<br />
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" valign="top"><strong>RSS 1.0</strong> (December 2000)<br /><a href="http://groups.yahoo.com/group/rss-dev/files/specification.html">http://groups.yahoo.com/group/rss-dev/files/specification.html</a> (RSS-DEV Working Group)<br />
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" valign="top"><strong>RSS 0.91</strong> (July 1999)<br /><a href="http://backend.userland.com/rss091">http://backend.userland.com/rss091</a> (Userland)<br />
+ <a href="http://www.purplepages.ie/RSS/netscape/rss0.91.html">http://www.purplepages.ie/RSS/netscape/rss0.91.html</a> (Netscape)<br />
+ <a href="http://my.netscape.com/publish/formats/rss-spec-0.91.html">http://my.netscape.com/publish/formats/rss-spec-0.91.html</a> (Netscape, Revision 3)<br />
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" valign="top"><strong>RSS 0.90</strong> (March 1999)<br /><a href="http://www.purplepages.ie/RSS/netscape/rss0.90.html">http://www.purplepages.ie/RSS/netscape/rss0.90.html</a> (Netscape)<br />
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="validators">RSS Validators</a>:<br />&nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content"><a href="http://aggregator.userland.com/validator">http://aggregator.userland.com/validator</a> (RSS 0.91, RSS 0.92)</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content"><a href="http://www.bath.ac.uk/~ccslrd/rss_validator/1.0/">http://www.bath.ac.uk/~ccslrd/rss_validator/1.0/</a> (RSS 1.0)</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content"><a href="http://www.bath.ac.uk/~ccslrd/rss_validator/">http://www.bath.ac.uk/~ccslrd/rss_validator/</a> (RSS 0.9)</td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <strong><a name="tutorials">RSS Tutorials - The Basics:</a></strong> (See also <a href="#specifications">RSS Specifications</a>, <a href="#websites">Websites</a>)<br />&nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"> - <a href="http://www.oreillynet.com/pub/a/network/4000/08/25/magazine/rss_tut.html">A step-by-step guide to building an RSS 1.0 document from the O'Reilly Network.</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"> - <a href="http://publishing.about.com/arts/publishing/library/blrss.htm">An easy to understand introduction to RSS 0.91 from About.com.</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"> - <a href="http://webreference.com/xml/column13/index.html">A comprehensive guide to creating RSS 0.91 files from Webreference.</a></td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="tutorialsgen">RSS Tutorials - Generating RSS</a>:<br />&nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">Active Server Pages (ASP)<br />
+ <a href="http://www.purplepages.ie/site/articles/article.asp?faq=6&amp;fldAuto=76">An article explaining how RSS files can be generated using ASP.</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">Perl<br />
+ <a href="http://www.webtechniques.com/archives/2000/02/eisenzopf/">Jonathan Eisenzopf explains how his XML::RSS module can be used to create an RSS file.</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">PHP<br />
+ <a href="http://linux.gelrevision.nl/php/">phpChannel, a set of two PHP class files to write rss files.</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="tools">RSS Tools &amp; Utilities</a>:
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ Aaron Swartz provides a useful online utility called <a href="http://logicerror.com/blogifyYourPage">BlogifyYourPage</a>, that makes it easy to produce an RSS 1.0 file for any page.<br /><br />
+ The <a href="http://www.webreference.com/perl/tools/">RSS Channel Editor</a> is a simple Perl CGI script that makes it easy to maintain an RSS channel. It can be used online at Webreference and you can also download the source.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="#top">top</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="examples">RSS Examples</a>:
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="http://newsfeeds.manilasites.com/">Newsfeeds</a> reviews sources of RSS files, good examples and ideas you can use in putting together your own feed.<br /><br />
+ <a href="http://www.ourfavoritesongs.com/">OurFavoriteSongs.Com</a> is a source of popular syndicated files, the top picks of <a href="http://radio.userland.com/">Radio Userland</a> users.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a name="promoterss"><strong>5. How can I promote my RSS file?</strong></a>
+ <br /><br />There are a couple of important places to register RSS files, firstly <a href="http://www.xmltree.com/">XMLTree.com</a>, a specialist directory of XML content, and secondly <a href="http://my.userland.com/">My.Userland.Com</a>. Once an RSS file has been included in these sources it is likely to be found by websites, online news portals or news reading applications seeking RSS content.<br /><br />
+ Websites should also create an information page, about syndicating their headlines. This will make existing users aware that the website has an RSS file so they can add it to their news reading applications or even include it on their own websites.<br /><br />
+ This information page will be indexed by regular search engines and can also be submitted to various niche directories:
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.4freecontent.com/">4FreeContent</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.findsticky.com/">FindSticky</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.freesticky.com/">FreeSticky</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://newsfeeds.manilasites.com/">Newsfeeds</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.purplepages.ie/site/content/">Purple Pages</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.woodoggy.com/">WooDoggy</a></td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ Websites that are interested in having their headlines picked up by organisations that aggregate headline content may also wish to visit:
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.linkyournews.com/">LinkYourNews.com</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.magportal.com/">MagPortal.com</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.moreover.com/">Moreover.com</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.newsnow.co.uk/">NewsNow.co.uk</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.newsisfree.com/">NewsIsFree.com</a></td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="morerss">6. Where can I find more information about RSS?</a><br /><br />
+ <a name="websites">Websites</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.oreillynet.com/rss/">O'Reilly DevCenter RSS</a> - Articles about RSS from the O'Reilly Network.</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://blogspace.com/rss/">RSS Info</a> - News and information on the RSS format</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://rsswhys.weblogger.com/">RSS Why?s</a> - A site that aims to objectively and concisely explore all the points surrounding the creation, maintenance, and history of RSS.</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.webreference.com/authoring/languages/xml/rss/">WebReference RSS Articles</a> - A collection of RSS articles and resources from Webreference.</td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ Discussion Lists
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://groups.yahoo.com/group/reallySimpleSyndication">ReallySimpleSyndication</a> - RSS 0.93.</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://groups.yahoo.com/group/rss-dev">RSS-DEV</a> - RSS 1.0.</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://groups.yahoo.com/group/syndication">Syndication</a> - XML syndication, mainly RSS 0.91.</td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ More RSS FAQs
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.voidstar.com/rssfaq">RSS FAQ</a> - A detailed RSS FAQ from Julian Bond, readers can also contribute.</td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="#top">top</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentsmall" colspan="2"><br /><br /><a href="http://www.rssfaq.com/">RSSFAQ</a> Copyright &copy; 2001 Members of the Syndication, RSS-DEV and ReallySimpleSyndication Groups.</td>
+ </tr>
+ <tr>
+ <td width="620" class="contentsmall" colspan="2">You may freely copy and distribute this document. Please give acknowledgements if you do.</td>
+ </tr>
+ <tr>
+ <td width="620" class="contentsmall" colspan="2">Last Updated: 24-August-2001 <a href="mailto:alis@purplepages.ie">Alis Marsden</a>.</td>
+ </tr>
+</table>
+</body>
+</html>
diff --git a/plugins/rssfeed/rss/testlibrss.cpp b/plugins/rssfeed/rss/testlibrss.cpp
new file mode 100644
index 0000000..5d98bba
--- /dev/null
+++ b/plugins/rssfeed/rss/testlibrss.cpp
@@ -0,0 +1,75 @@
+#include "testlibrss.h"
+
+#include "image.h"
+
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kapplication.h>
+#include <kdebug.h>
+
+using namespace RSS;
+
+static const KCmdLineOptions options[] =
+{
+ { "+url", I18N_NOOP("URL of feed"), 0 },
+ KCmdLineLastOption
+};
+
+
+void Tester::test( const QString &url )
+{
+ Loader *loader = Loader::create();
+ connect( loader, SIGNAL( loadingComplete( Loader *, Document, Status ) ),
+ this, SLOT( slotLoadingComplete( Loader *, Document, Status ) ) );
+ loader->loadFrom( url, new FileRetriever );
+}
+
+void Tester::slotLoadingComplete( Loader *loader, Document doc, Status status )
+{
+ if ( status == Success )
+ {
+ kdDebug() << "Successfully retrieved '" << doc.title() << "'" << endl;
+ kdDebug() << doc.description() << endl;
+
+ if ( doc.image() ) {
+ kdDebug() << "Image: ";
+ kdDebug() << " Title: " << doc.image()->title() << endl;
+ kdDebug() << " URL: " << doc.image()->url() << endl;
+ kdDebug() << " Link: " << doc.image()->link() << endl;
+ }
+
+ kdDebug() << "Articles:" << endl;
+
+ Article::List list = doc.articles();
+ Article::List::ConstIterator it;
+ Article::List::ConstIterator en=list.end();
+ for (it = list.begin(); it != en; ++it)
+ {
+ kdDebug() << "\tTitle: " << (*it).title() << endl;
+ kdDebug() << "\tText: " << (*it).description() << endl;
+ }
+ }
+
+ if ( status != Success )
+ kdDebug() << "ERROR " << loader->errorCode() << endl;
+
+ kapp->quit();
+}
+
+int main( int argc, char **argv )
+{
+ KAboutData aboutData( "testlibrss", "testlibrss", "0.1" );
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options );
+ KApplication app;
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ if ( args->count() != 1 ) args->usage();
+
+ Tester tester;
+ tester.test( args->arg( 0 ) );
+
+ return app.exec();
+}
+
+#include "testlibrss.moc"
diff --git a/plugins/rssfeed/rss/testlibrss.h b/plugins/rssfeed/rss/testlibrss.h
new file mode 100644
index 0000000..c65fa3b
--- /dev/null
+++ b/plugins/rssfeed/rss/testlibrss.h
@@ -0,0 +1,25 @@
+#ifndef TESTLIBRSS_H
+#define TESTLIBRSS_H
+
+#include <qobject.h>
+
+#include "loader.h"
+#include "document.h"
+#include "article.h"
+#include "global.h"
+
+using RSS::Loader;
+using RSS::Document;
+using RSS::Status;
+
+class Tester : public QObject
+{
+ Q_OBJECT
+ public:
+ void test( const QString &url );
+
+ private slots:
+ void slotLoadingComplete( Loader *loader, Document doc, Status status );
+};
+
+#endif
diff --git a/plugins/rssfeed/rss/textinput.cpp b/plugins/rssfeed/rss/textinput.cpp
new file mode 100644
index 0000000..432b773
--- /dev/null
+++ b/plugins/rssfeed/rss/textinput.cpp
@@ -0,0 +1,96 @@
+/*
+ * textinput.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "textinput.h"
+#include "tools_p.h"
+
+#include <kurl.h>
+
+#include <qdom.h>
+
+using namespace RSS;
+
+struct TextInput::Private : public Shared
+{
+ QString title;
+ QString description;
+ QString name;
+ KURL link;
+};
+
+TextInput::TextInput() : d(new Private)
+{
+}
+
+TextInput::TextInput(const TextInput &other) : d(0)
+{
+ *this = other;
+}
+
+TextInput::TextInput(const QDomNode &node) : d(new Private)
+{
+ QString elemText;
+
+ if (!(elemText = extractNode(node, QString::fromLatin1("title"))).isNull())
+ d->title = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("description"))).isNull())
+ d->description = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("name"))))
+ d->name = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("link"))).isNull())
+ d->link = elemText;
+}
+
+TextInput::~TextInput()
+{
+ if (d->deref())
+ delete d;
+}
+
+QString TextInput::title() const
+{
+ return d->title;
+}
+
+QString TextInput::description() const
+{
+ return d->description;
+}
+
+QString TextInput::name() const
+{
+ return d->name;
+}
+
+const KURL &TextInput::link() const
+{
+ return d->link;
+}
+
+TextInput &TextInput::operator=(const TextInput &other)
+{
+ if (this != &other) {
+ other.d->ref();
+ if (d && d->deref())
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+bool TextInput::operator==(const TextInput &other) const
+{
+ return d->title == other.title() &&
+ d->description == other.description() &&
+ d->name == other.name() &&
+ d->link == other.link();
+}
+
+// vim:noet:ts=4
diff --git a/plugins/rssfeed/rss/textinput.h b/plugins/rssfeed/rss/textinput.h
new file mode 100644
index 0000000..dd13c42
--- /dev/null
+++ b/plugins/rssfeed/rss/textinput.h
@@ -0,0 +1,121 @@
+/*
+ * textinput.h
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_TEXTINPUT_H
+#define LIBRSS_TEXTINPUT_H
+
+#include "global.h"
+
+class KURL;
+
+class QDomNode;
+class QString;
+
+namespace RSS
+{
+ /**
+ * Represents a text input facility as stored in a RSS file for the purpose
+ * of allowing users to submit queries back to the publisher's site. You
+ * don't have to instantiate one of these yourself, the common way to access
+ * instances is via Document::textInput().
+ * @see Document::textInput()
+ */
+ class TextInput
+ {
+ public:
+ /**
+ * Default constructor.
+ */
+ TextInput();
+
+ /**
+ * Copy constructor.
+ * @param other The TextInput object to copy.
+ */
+ TextInput(const TextInput &other);
+
+ /**
+ * Constructs a TextInput from a piece of RSS markup.
+ * @param node A QDomNode which references the DOM leaf to be used
+ * for constructing the TextInput.
+ */
+ TextInput(const QDomNode &node);
+
+ /**
+ * Assignment operator.
+ * @param other The TextInput object to clone.
+ * @return A reference to the cloned TextInput object.
+ */
+ TextInput &operator=(const TextInput &other);
+
+ /**
+ * Compares two text inputs. Two text inputs are considered
+ * identical if their properties (title, description, link etc.)
+ * are identical.
+ * @param other The text input to compare with.
+ * @return Whether the two text inputs are equal.
+ */
+ bool operator==(const TextInput &other) const;
+
+ /**
+ * Convenience method. Simply calls !operator==().
+ * @param other The text input to compared with.
+ * @return Whether the two text inputs are unequal.
+ */
+ bool operator!=(const TextInput &other) const { return !operator==(other); }
+
+ /**
+ * Destructor.
+ */
+ virtual ~TextInput();
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The title (often a label to be used for the input field)
+ * of the text input, or QString::null if no title is available.
+ */
+ QString title() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The description (usually used as a tooltip which appears
+ * if the mouse hovers above the input field for a short time) of
+ * the text input, or QString::null if no description is
+ * available.
+ */
+ QString description() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The name of the text input (what's this for?) of the
+ * text input, or QString::null, if no name is available.
+ */
+ QString name() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A link to which the contents of the input field should
+ * be sent after the user specified them. This is often a CGI
+ * program on a remote server which evaluates the entered
+ * information. An empty KURL is returned in case no link is
+ * available.
+ * Note that the RSS 0.91 Specification dictates that URLs not
+ * starting with "http://" or "ftp://" are considered invalid.
+ */
+ const KURL &link() const;
+
+ private:
+ struct Private;
+ Private *d;
+ };
+}
+
+#endif // LIBRSS_TEXTINPUT_H
+// vim: noet:ts=4
diff --git a/plugins/rssfeed/rss/tools_p.cpp b/plugins/rssfeed/rss/tools_p.cpp
new file mode 100644
index 0000000..c1ebbc9
--- /dev/null
+++ b/plugins/rssfeed/rss/tools_p.cpp
@@ -0,0 +1,51 @@
+/*
+ * tools_p.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "tools_p.h"
+
+#include <krfcdate.h>
+#include <qdom.h>
+
+time_t RSS::parseISO8601Date(const QString &s)
+{
+ // do some sanity check: 26-12-2004T00:00+00:00 is parsed to epoch+1 in the KRFCDate, which is wrong. So let's check if the date begins with YYYY -fo
+ if (s.stripWhiteSpace().left(4).toInt() < 1000)
+ return 0; // error
+
+ // FIXME: imho this is done in KRFCDate::parseDateISO8601() automatically, so we could omit it? -fo
+ if (s.find('T') != -1)
+ return KRFCDate::parseDateISO8601(s);
+ else
+ return KRFCDate::parseDateISO8601(s + "T12:00:00");
+}
+
+
+QString RSS::extractNode(const QDomNode &parent, const QString &elemName, bool isInlined)
+{
+ QDomNode node = parent.namedItem(elemName);
+ if (node.isNull())
+ return QString::null;
+
+ QString result = node.toElement().text();
+
+ bool hasPre = result.contains("<pre>",false);
+ bool hasHtml = hasPre || result.contains("<"); // FIXME: test if we have html, should be more clever -> regexp
+ if(!isInlined && !hasHtml) // perform nl2br if not a inline elt and it has no html elts
+ result = result = result.replace(QChar('\n'), "<br />");
+ if(!hasPre) // strip white spaces if no <pre>
+ result = result.simplifyWhiteSpace();
+
+ if (result.isEmpty())
+ return QString::null;
+
+ return result;
+}
+
+// vim:noet:ts=4
diff --git a/plugins/rssfeed/rss/tools_p.h b/plugins/rssfeed/rss/tools_p.h
new file mode 100644
index 0000000..5076004
--- /dev/null
+++ b/plugins/rssfeed/rss/tools_p.h
@@ -0,0 +1,34 @@
+/*
+ * tools_p.h
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_TOOLS_P_H
+#define LIBRSS_TOOLS_P_H
+
+#include <time.h>
+
+class QDomNode;
+class QString;
+
+namespace RSS
+{
+ struct Shared
+ {
+ Shared() : count(1) { }
+ void ref() { count++; }
+ bool deref() { return !--count; }
+ unsigned int count;
+ };
+
+ QString extractNode(const QDomNode &parent, const QString &elemName, bool isInlined=true);
+ time_t parseISO8601Date(const QString &s);
+}
+
+#endif // LIBRSS_TOOLS_P_H
+// vim:noet:ts=4
diff --git a/plugins/rssfeed/rssarticle.cpp b/plugins/rssfeed/rssarticle.cpp
new file mode 100644
index 0000000..afc932d
--- /dev/null
+++ b/plugins/rssfeed/rssarticle.cpp
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "rssarticle.h"
+
+namespace kt
+{
+
+ RssArticle::RssArticle( )
+ {
+
+ }
+
+ RssArticle::RssArticle(RSS::Article article)
+ {
+ //these lines generate errors when compiling
+ m_title = article.title();
+ m_link = article.link();
+ m_description = article.description();
+ m_pubDate = article.pubDate();
+ m_guid = article.guid();
+ m_downloaded = 0;
+ }
+
+ RssArticle::RssArticle(const RssArticle &other)
+ {
+ *this = other;
+ }
+
+ RssArticle::RssArticle(QString title, KURL link, QString description, QDateTime pubDate, QString guid, int downloaded)
+ {
+ m_title = title;
+ m_link = link;
+ m_description = description;
+ m_pubDate = pubDate;
+ m_guid = guid;
+ m_downloaded = downloaded;
+ }
+
+ RssArticle &RssArticle::operator=(const RssArticle &other)
+ {
+ if (&other != this)
+ {
+ m_title = other.title();
+ m_link = other.link();
+ m_description = other.description();
+ m_pubDate = other.pubDate();
+ m_guid = other.guid();
+ m_downloaded = other.downloaded();
+ }
+ return *this;
+ }
+
+ bool RssArticle::operator==(const RssArticle &other) const
+ {
+ //let's try just using the guid for now as it should be sufficient
+ //return m_title==other.title() && m_link==other.link() && m_description==other.description() && m_pubDate==other.pubDate();
+ return m_guid==other.guid();
+ }
+
+ QDataStream &operator<<( QDataStream &out, const RssArticle &article )
+ {
+ out << article.title() << article.link() << article.description() << article.pubDate() << article.guid() << article.downloaded();
+
+ return out;
+ }
+
+ QDataStream &operator>>( QDataStream &in, RssArticle &article )
+ {
+ KURL link;
+ QString title;
+ QString description;
+ QDateTime pubDate;
+ QString guid;
+ int downloaded;
+ in >> title >> link >> description >> pubDate >> guid >> downloaded;
+ article = RssArticle(title, link, description, pubDate, guid, downloaded);
+
+ return in;
+ }
+
+ RssArticle::~RssArticle()
+ {
+
+ }
+
+}
diff --git a/plugins/rssfeed/rssarticle.h b/plugins/rssfeed/rssarticle.h
new file mode 100644
index 0000000..4d8c536
--- /dev/null
+++ b/plugins/rssfeed/rssarticle.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef RSSARTICLE_H
+#define RSSARTICLE_H
+
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qdatastream.h>
+
+#include <kurl.h>
+
+#include "rss/article.h"
+
+namespace kt
+{
+ /**
+ * @brief RssFeed Manager Class
+ * @author Alan Jones <skyphyr@gmail.com>
+ *
+ *
+ */
+
+ class RssArticle
+ {
+ public:
+
+ typedef QValueList<RssArticle> List;
+
+ /**
+ * Default constructor.
+ */
+ RssArticle();
+ RssArticle(RSS::Article article);
+ RssArticle(const RssArticle &other);
+ RssArticle &operator=(const RssArticle &other);
+ bool operator==(const RssArticle &other) const;
+ RssArticle(QString title, KURL link, QString description, QDateTime pubDate, QString guid, int downloaded = 0);
+
+ void setTitle(const QString& title) { m_title=title; }
+ void setDownloaded(const int downloaded) { m_downloaded=downloaded; }
+
+ QString title() const { return m_title; }
+ KURL link() const { return m_link; }
+ QString description() const { return m_description; }
+ QDateTime pubDate() const { return m_pubDate; }
+ QString guid() const { return m_guid; }
+ int downloaded() const { return m_downloaded; }
+
+ ~RssArticle();
+
+ private:
+ KURL m_link;
+ QString m_title;
+ QString m_description;
+ QDateTime m_pubDate;
+ QString m_guid;
+ int m_downloaded;
+ };
+
+ QDataStream &operator<<( QDataStream &out, const RssArticle &article );
+ QDataStream &operator>>( QDataStream &in, RssArticle &article );
+
+}
+
+#endif
diff --git a/plugins/rssfeed/rssfeed.cpp b/plugins/rssfeed/rssfeed.cpp
new file mode 100644
index 0000000..0d1244b
--- /dev/null
+++ b/plugins/rssfeed/rssfeed.cpp
@@ -0,0 +1,359 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "rssfeed.h"
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <krfcdate.h>
+#include <kio/netaccess.h>
+#include <qfile.h>
+#include <qapplication.h>
+#include <qdir.h>
+
+namespace kt
+{
+
+ void RssFeed::startFeed()
+ {
+ if (m_active)
+ {
+ refreshFeed();
+ refreshTimer.start(QTime().msecsTo(m_autoRefresh));
+ }
+ else
+ {
+ refreshTimer.stop();
+ }
+ }
+
+ void RssFeed::initialize()
+ {
+ feedLoading = false;
+ loadArticles();
+
+ connect(&refreshTimer, SIGNAL(timeout()), this, SLOT( refreshFeed() ) );
+ connect(this, SIGNAL(articlesChanged(const RssArticle::List&)), this, SLOT( saveArticles() ) );
+
+ startFeed();
+ }
+
+ RssFeed::RssFeed(QObject * parent) : QObject(parent)
+ {
+ m_active = false;
+ m_articleAge = 365;
+ m_ignoreTTL = false;
+ m_title = "New";
+
+ initialize();
+ }
+
+ RssFeed::RssFeed(KURL feedUrl, QString title, bool active, int articleAge, bool ignoreTTL, QTime autoRefresh )
+ {
+ m_feedUrl = feedUrl;
+ m_title = title;
+ m_active = active;
+ m_articleAge = articleAge;
+ m_ignoreTTL = ignoreTTL;
+ m_autoRefresh = autoRefresh;
+
+ initialize();
+ }
+
+ RssFeed::RssFeed(const RssFeed &other) : QObject()
+ {
+ *this = other;
+ }
+
+ RssFeed &RssFeed::operator=(const RssFeed &other)
+ {
+ if (&other != this)
+ {
+ m_feedUrl = other.feedUrl();
+ m_title = other.title();
+ m_active = other.active();
+ m_articleAge = other.articleAge();
+ m_ignoreTTL = other.ignoreTTL();
+ m_autoRefresh = other.autoRefresh();
+ }
+
+ initialize();
+
+ return *this;
+ }
+
+ void RssFeed::setFeedUrl( const KURL& url )
+ {
+ if (m_feedUrl != url)
+ {
+ m_feedUrl = url;
+ loadArticles();
+ startFeed();
+ emit feedUrlChanged(url);
+ }
+ }
+
+ void RssFeed::setFeedUrl( const QString& url )
+ {
+ if (m_feedUrl != url)
+ {
+ m_feedUrl = url;
+ loadArticles();
+ startFeed();
+ emit feedUrlChanged(url);
+ }
+ }
+
+ void RssFeed::setActive( bool active )
+ {
+ if (m_active != active)
+ {
+ m_active = active;
+
+ startFeed();
+
+ emit activeChanged(active);
+ }
+ }
+
+ void RssFeed::setArticleAge( int articleAge )
+ {
+ if (m_articleAge != articleAge)
+ {
+ if (articleAge < m_articleAge)
+ {
+ cleanArticles();
+ }
+
+ m_articleAge = articleAge;
+ emit articleAgeChanged(articleAge);
+ }
+ }
+
+ void RssFeed::setTitle( const QString& title )
+ {
+ if (m_title != title)
+ {
+ m_title = title;
+ emit titleChanged(title);
+ }
+ }
+
+ void RssFeed::setAutoRefresh( const QTime& autoRefresh )
+ {
+ if (m_autoRefresh != autoRefresh)
+ {
+ m_autoRefresh = autoRefresh;
+ if (m_active)
+ {
+ refreshTimer.changeInterval(QTime().msecsTo(m_autoRefresh));
+ }
+
+ emit autoRefreshChanged(autoRefresh);
+ }
+ }
+
+ void RssFeed::setIgnoreTTL( bool ignoreTTL )
+ {
+ if (m_ignoreTTL != ignoreTTL)
+ {
+ m_ignoreTTL = ignoreTTL;
+ emit ignoreTTLChanged(ignoreTTL);
+ }
+ }
+
+ QString RssFeed::getFilename()
+ {
+ QDir directory;
+ directory.mkdir(KGlobal::dirs()->saveLocation("data","ktorrent") + "rssfeeds");
+ return KGlobal::dirs()->saveLocation("data","ktorrent") + "rssfeeds/" + m_feedUrl.prettyURL(-1).replace("/", "_").replace(":", "_") + ".ktr";
+
+ }
+
+ void RssFeed::loadArticles()
+ {
+ QString filename = getFilename();
+
+ //load articles from disk
+ QFile file(filename);
+
+ if (file.exists())
+ {
+ file.open( IO_ReadOnly );
+ QDataStream in(&file);
+
+ in >> m_articles;
+ emit articlesChanged( m_articles );
+ }
+ }
+
+ void RssFeed::saveArticles()
+ {
+ QString filename = getFilename();
+
+ //load articles from disk
+ QFile file(filename);
+
+ file.open( IO_WriteOnly );
+ QDataStream out(&file);
+
+ out << m_articles;
+ }
+
+ void RssFeed::cleanArticles()
+ {
+ bool removed = false;
+
+ RssArticle::List::iterator it;
+ for ( it = m_articles.begin(); it != m_articles.end(); )
+ {
+ if ((*it).pubDate().daysTo(QDateTime::currentDateTime()) > m_articleAge)
+ {
+ it = m_articles.erase(it);
+ removed = true;
+ }
+ else
+ {
+ it++;
+ }
+ }
+
+ if (removed)
+ {
+ emit articlesChanged(m_articles);
+ }
+
+ }
+
+ void RssFeed::clearArticles()
+ {
+ m_articles.clear();
+ }
+
+ void RssFeed::refreshFeed()
+ {
+ if (feedLoading)
+ return;
+
+ feedLoading = true;
+ cleanArticles();
+ Loader * feedLoader = Loader::create();
+ connect( feedLoader, SIGNAL( loadingComplete( Loader *, Document, Status ) ),
+ this, SLOT( feedLoaded( Loader *, Document, Status ) ) );
+ feedLoader->loadFrom( m_feedUrl, new FileRetriever );
+ }
+
+ void RssFeed::feedLoaded(Loader *feedLoader, Document doc, Status status)
+ {
+ feedLoading = false;
+
+ if ( status == Success )
+ {
+ bool added = false;
+
+ if (m_title.isEmpty() || m_title == QString("New"))
+ {
+ setTitle(doc.title());
+ emit updateTitle(doc.title());
+ }
+
+ if (!m_ignoreTTL)
+ {
+ if (doc.ttl() < 0)
+ {
+ setAutoRefresh(QTime().addSecs(3600));
+ }
+ else
+ {
+ setAutoRefresh(QTime().addSecs(doc.ttl() * 60));
+ }
+ }
+
+ RssArticle curArticle;
+
+ for (int i=doc.articles().count()-1; i>=0; i--)
+ {
+ curArticle = doc.articles()[i];
+ if (curArticle.pubDate().daysTo(QDateTime::currentDateTime()) < m_articleAge && !m_articles.contains(curArticle))
+ {
+ m_articles.prepend(curArticle);
+ emit scanRssArticle(curArticle);
+ added = true;
+ }
+ }
+
+ if (added)
+ {
+ emit articlesChanged(m_articles);
+ }
+ } else {
+ qDebug( "There was and error loading the feed\n");
+ }
+
+ disconnect( feedLoader, SIGNAL( loadingComplete( Loader *, Document, Status ) ),
+ this, SLOT( feedLoaded( Loader *, Document, Status ) ) );
+ feedLoader->deleteLater();
+
+ }
+
+ void RssFeed::setDownloaded(QString link, int downloaded)
+ {
+ bool changed = false;
+
+ RssArticle::List::iterator it;
+ for ( it = m_articles.begin(); it != m_articles.end(); it++ )
+ {
+ if ((*it).link().prettyURL() == link)
+ {
+ (*it).setDownloaded( downloaded );
+ changed = true;
+ }
+ }
+
+ if (changed)
+ {
+ emit articlesChanged(m_articles);
+ }
+ }
+
+ QDataStream &operator<<( QDataStream &out, const RssFeed &feed )
+ {
+ out << feed.feedUrl() << feed.title() << int(feed.active()) << feed.articleAge() << int(feed.ignoreTTL()) << feed.autoRefresh();
+
+ return out;
+ }
+
+ QDataStream &operator>>( QDataStream &in, RssFeed &feed )
+ {
+ KURL feedUrl;
+ QString title;
+ int active;
+ int articleAge;
+ int ignoreTTL;
+ QTime autoRefresh;
+ in >> feedUrl >> title >> active >> articleAge >> ignoreTTL >> autoRefresh;
+ feed = RssFeed(feedUrl, title, active, articleAge, ignoreTTL, autoRefresh);
+
+ return in;
+ }
+
+ RssFeed::~RssFeed()
+ {
+ }
+}
diff --git a/plugins/rssfeed/rssfeed.h b/plugins/rssfeed/rssfeed.h
new file mode 100644
index 0000000..9bcd2f7
--- /dev/null
+++ b/plugins/rssfeed/rssfeed.h
@@ -0,0 +1,127 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef RSSFEED_H
+#define RSSFEED_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qvaluelist.h>
+#include <qtimer.h>
+#include <qdatastream.h>
+
+#include <kurl.h>
+
+#include "rss/loader.h"
+#include "rss/document.h"
+
+#include "rssarticle.h"
+
+using namespace RSS;
+
+namespace kt
+{
+ /**
+ * @brief RssFeed Class
+ * @author Alan Jones <skyphyr@gmail.com>
+ *
+ *
+ */
+
+ class RssFeed : public QObject
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Default constructor.
+ */
+ RssFeed(QObject * parent = 0);
+ RssFeed(KURL feedUrl, QString title = "", bool active = false, int articleAge = 3, bool ignoreTTL = false, QTime autoRefresh = QTime());
+ RssFeed(const RssFeed &other);
+ RssFeed &operator=(const RssFeed &other);
+ ~RssFeed();
+
+ KURL feedUrl() const { return m_feedUrl; }
+ bool active() const { return m_active; }
+ int articleAge() const { return m_articleAge; }
+ QString title() const { return m_title; }
+ QTime autoRefresh() const { return m_autoRefresh; }
+ bool ignoreTTL() const { return m_ignoreTTL; }
+
+
+ RssArticle::List articles() const { return m_articles; }
+
+
+ public slots:
+ void refreshFeed();
+ void feedLoaded(Loader *feedLoader, Document doc, Status status);
+
+ void clearArticles();
+
+ void setFeedUrl( const KURL& url );
+ void setFeedUrl( const QString& url );
+ void setActive( bool active );
+ void setArticleAge( int articleAge );
+ void setTitle( const QString& title );
+ void setAutoRefresh( const QTime& autoRefresh );
+ void setIgnoreTTL( bool ignoreTTL );
+ void saveArticles();
+
+ void setDownloaded(QString link, int downloaded);
+
+ signals:
+ void feedUrlChanged( const KURL& url );
+ void activeChanged( bool active );
+ void articleAgeChanged( int articleAge );
+ void titleChanged( const QString& title );
+ void updateTitle( const QString& title );
+ void autoRefreshChanged( const QTime& autoRefresh );
+ void ignoreTTLChanged( bool ignoreTTL );
+
+ void articlesChanged( const RssArticle::List& articles );
+
+ void scanRssArticle( RssArticle article );
+
+ private:
+ KURL m_feedUrl;
+ bool m_active;
+ int m_articleAge;
+ QString m_title;
+ QTime m_autoRefresh;
+ bool m_ignoreTTL;
+ RssArticle::List m_articles;
+ QTimer refreshTimer;
+
+ bool feedLoading;
+
+ QString getFilename();
+ void initialize();
+ void startFeed();
+ void cleanArticles();
+ void loadArticles();
+ };
+
+ QDataStream &operator<<( QDataStream &out, const RssFeed &feed );
+ QDataStream &operator>>( QDataStream &in, RssFeed &feed );
+
+}
+
+#endif
diff --git a/plugins/rssfeed/rssfeedmanager.cpp b/plugins/rssfeed/rssfeedmanager.cpp
new file mode 100644
index 0000000..106ca0f
--- /dev/null
+++ b/plugins/rssfeed/rssfeedmanager.cpp
@@ -0,0 +1,1318 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "rssfeedmanager.h"
+
+#include <kdirlister.h>
+#include <kfileitem.h>
+#include <klocale.h>
+#include <kio/netaccess.h>
+#include <kstandarddirs.h>
+#include <keditlistbox.h>
+// #include <kmimetype.h>
+#include <kmessagebox.h>
+
+#include <qstring.h>
+#include <qobject.h>
+#include <qfile.h>
+#include <qdatetime.h>
+
+#include <qlineedit.h>
+#include <kurlrequester.h>
+#include <qspinbox.h>
+#include <qcheckbox.h>
+#include <qdatetimeedit.h>
+#include <qtable.h>
+#include <qregexp.h>
+#include <qlayout.h>
+
+#include <torrent/globals.h>
+#include <util/log.h>
+#include <util/constants.h>
+
+#include <interfaces/coreinterface.h>
+
+#include <qapplication.h>
+
+#include "../../libktorrent/torrent/bdecoder.h"
+#include "../../libktorrent/torrent/bnode.h"
+
+#include "rsslinkdownloader.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ RssFeedManager::RssFeedManager(CoreInterface* core, QWidget * parent) : RssFeedWidget(parent)
+ {
+ //Construct the manager
+ m_core = core;
+ currentFeed = -1;
+ currentAcceptFilter = -1;
+ currentRejectFilter = -1;
+
+ feedListSaving = false;
+ filterListSaving = false;
+
+ //get the articles list setup
+ feedArticles->setLeftMargin(0);
+ feedArticles->verticalHeader()->hide();
+ feedArticles->setNumCols(3);
+ feedArticles->setColumnLabels(QStringList() << i18n("Title") << i18n("Description") << i18n("Link"));
+ feedArticles->horizontalHeader()->setStretchEnabled(true, 0);
+ feedArticles->hideColumn(1);
+ feedArticles->hideColumn(2);
+
+ //get the matches list setup
+ filterMatches->setLeftMargin(0);
+ filterMatches->verticalHeader()->hide();
+ filterMatches->setNumCols(4);
+ filterMatches->setColumnLabels(QStringList() << i18n("Season") << i18n("Episode") << i18n("Time") << i18n("Link"));
+ filterMatches->setColumnWidth(0, 60);
+ filterMatches->setColumnWidth(1, 60);
+ filterMatches->setColumnWidth(2, 180);
+ filterMatches->horizontalHeader()->setStretchEnabled(true, 3);
+
+ loadFeedList();
+ loadFilterList();
+
+ //connect the buttons
+ connect(newFeed, SIGNAL(clicked()), this, SLOT(addNewFeed() ) );
+ connect(deleteFeed, SIGNAL(clicked()), this, SLOT(deleteSelectedFeed() ) );
+
+ connect(newAcceptFilter, SIGNAL(clicked()), this, SLOT(addNewAcceptFilter() ) );
+ connect(deleteAcceptFilter, SIGNAL(clicked()), this, SLOT(deleteSelectedAcceptFilter() ) );
+
+ connect(newRejectFilter, SIGNAL(clicked()), this, SLOT(addNewRejectFilter() ) );
+ connect(deleteRejectFilter, SIGNAL(clicked()), this, SLOT(deleteSelectedRejectFilter() ) );
+
+ //connect the changing of the active feed
+ connect(feedlist, SIGNAL(selectionChanged()), this, SLOT(changedActiveFeed()) );
+
+ //connect the changing of the url to enable the refresh button
+ connect(feedUrl, SIGNAL(textChanged(const QString &)), this, SLOT(changedFeedUrl()) );
+
+ //connect the changing of the filters
+ connect(acceptFilterList, SIGNAL(selectionChanged()), this, SLOT(changedActiveAcceptFilter()) );
+ connect(rejectFilterList, SIGNAL(selectionChanged()), this, SLOT(changedActiveRejectFilter()) );
+
+ //connect the selection and downloading of articles
+ connect(feedArticles, SIGNAL(selectionChanged()), this, SLOT(changedArticleSelection()) );
+ connect(downloadArticle, SIGNAL(clicked()), this, SLOT(downloadSelectedArticles()) );
+
+ //connect the selection, downloading and deletion of matches
+ connect(filterMatches, SIGNAL(selectionChanged()), this, SLOT(changedMatchSelection()) );
+ connect(downloadFilterMatch, SIGNAL(clicked()), this, SLOT(downloadSelectedMatches()) );
+ connect(deleteFilterMatch, SIGNAL(clicked()), this, SLOT(deleteSelectedMatches()) );
+
+ //connect the test text update to the slot
+ connect(testText, SIGNAL(textChanged(const QString &)), this, SLOT(testTextChanged()) );
+ connect(testTestText, SIGNAL(clicked()), this, SLOT(testFilter()) );
+
+ changedActiveFeed();
+ changedActiveAcceptFilter();
+
+ }
+
+ RssFeedManager::~RssFeedManager()
+ {
+ //Destruct the manager
+ }
+
+ void RssFeedManager::clearArticles()
+ {
+ int pos = feeds.find((RssFeed *)sender());
+
+ if (pos >= 0)
+ {
+ feeds.at(pos)->clearArticles();
+ if (feedlist->isSelected(pos))
+ {
+ //this feed is active so we should update the display
+ feedArticles->setNumRows(0);
+ }
+ }
+ }
+
+ void RssFeedManager::changedFeedUrl()
+ {
+ refreshFeed->setEnabled(!feedUrl->url().isEmpty());
+ }
+
+ void RssFeedManager::connectFeed(int index)
+ {
+
+ connect(feedTitle, SIGNAL(textChanged(const QString &)), feeds.at(index), SLOT(setTitle(const QString &) ) );
+ connect(feeds.at(index), SIGNAL(titleChanged(const QString &)), this, SLOT(setFeedTitle(const QString &) ) );
+
+ //url
+ connect(feedUrl, SIGNAL(textChanged(const QString &)), feeds.at(index), SLOT(setFeedUrl(const QString&) ) );
+ connect(feeds.at(index), SIGNAL(feedUrlChanged(const KURL&)), feedUrl, SLOT(setKURL(const KURL&) ) );
+
+ //articleAge
+ connect(feedArticleAge, SIGNAL(valueChanged(int)), feeds.at(index), SLOT(setArticleAge(int) ) );
+ connect(feeds.at(index), SIGNAL(articleAgeChanged(int)), feedArticleAge, SLOT(setValue(int) ) );
+
+ //active
+ connect(feedActive, SIGNAL(toggled(bool)), feeds.at(index), SLOT(setActive(bool) ) );
+ connect(feeds.at(index), SIGNAL(activeChanged(bool)), feedActive, SLOT(setChecked(bool) ) );
+
+ //autoRefresh
+ connect(feedAutoRefresh, SIGNAL(valueChanged(const QTime&)), feeds.at(index), SLOT(setAutoRefresh(const QTime&) ) );
+ connect(feeds.at(index), SIGNAL(autoRefreshChanged(const QTime&)), feedAutoRefresh, SLOT(setTime(const QTime&) ) );
+
+ //ignoreTTL
+ connect(feedIgnoreTTL, SIGNAL(toggled(bool)), feeds.at(index), SLOT(setIgnoreTTL(bool) ) );
+ connect(feeds.at(index), SIGNAL(ignoreTTLChanged(bool)), feedIgnoreTTL, SLOT(setChecked(bool) ) );
+
+ //articles
+ connect(feeds.at(index), SIGNAL(articlesChanged(const RssArticle::List&)), this, SLOT(updateArticles(const RssArticle::List&) ) );
+
+ //connect the refresh button
+ connect(refreshFeed, SIGNAL(clicked()), feeds.at(index), SLOT(refreshFeed()) );
+ }
+
+ void RssFeedManager::disconnectFeed(int index)
+ {
+ disconnect(feedTitle, SIGNAL(textChanged(const QString &)), feeds.at(index), SLOT(setTitle(const QString &) ) );
+ disconnect(feeds.at(index), SIGNAL(titleChanged(const QString &)), this, SLOT(setFeedTitle(const QString &) ) );
+
+ //url
+ disconnect(feedUrl, SIGNAL(textChanged(const QString &)), feeds.at(index), SLOT(setFeedUrl(const QString&) ) );
+ disconnect(feeds.at(index), SIGNAL(feedUrlChanged(const KURL&)), feedUrl, SLOT(setKURL(const KURL&) ) );
+
+ //articleAge
+ disconnect(feedArticleAge, SIGNAL(valueChanged(int)), feeds.at(index), SLOT(setArticleAge(int) ) );
+ disconnect(feeds.at(index), SIGNAL(articleAgeChanged(int)), feedArticleAge, SLOT(setValue(int) ) );
+
+ //active
+ disconnect(feedActive, SIGNAL(toggled(bool)), feeds.at(index), SLOT(setActive(bool) ) );
+ disconnect(feeds.at(index), SIGNAL(activeChanged(bool)), feedActive, SLOT(setChecked(bool) ) );
+
+ //autoRefresh
+ disconnect(feedAutoRefresh, SIGNAL(valueChanged(const QTime&)), feeds.at(index), SLOT(setAutoRefresh(const QTime&) ) );
+ disconnect(feeds.at(index), SIGNAL(autoRefreshChanged(const QTime&)), feedAutoRefresh, SLOT(setTime(const QTime&) ) );
+
+ //ignoreTTL
+ disconnect(feedIgnoreTTL, SIGNAL(toggled(bool)), feeds.at(index), SLOT(setIgnoreTTL(bool) ) );
+ disconnect(feeds.at(index), SIGNAL(ignoreTTLChanged(bool)), feedIgnoreTTL, SLOT(setChecked(bool) ) );
+
+ //articles
+ disconnect(feeds.at(index), SIGNAL(articlesChanged(const RssArticle::List&)), this, SLOT(updateArticles(const RssArticle::List&) ) );
+
+ disconnect(refreshFeed, SIGNAL(clicked()), feeds.at(index), SLOT(refreshFeed()) );
+ }
+
+ void RssFeedManager::connectFilter(int index, bool acceptFilter)
+ {
+ if (acceptFilter)
+ {
+ //title
+ connect(filterTitle, SIGNAL(textChanged(const QString &)), acceptFilters.at(index), SLOT(setTitle(const QString &) ) );
+ connect(acceptFilters.at(index), SIGNAL(titleChanged(const QString &)), this, SLOT(setFilterTitle(const QString &) ) );
+ //active
+ connect(filterActive, SIGNAL(toggled(bool)), acceptFilters.at(index), SLOT(setActive(bool) ) );
+ connect(acceptFilters.at(index), SIGNAL(activeChanged(bool)), filterActive, SLOT(setChecked(bool) ) );
+ //regExps
+ connect(filterRegExps, SIGNAL(changed()), this, SLOT(updateRegExps()) );
+ //series
+ connect(filterSeries, SIGNAL(toggled(bool)), acceptFilters.at(index), SLOT(setSeries(bool) ) );
+ connect(acceptFilters.at(index), SIGNAL(seriesChanged(bool)), filterSeries, SLOT(setChecked(bool) ) );
+ //sansEpisode
+ connect(filterSansEpisode, SIGNAL(toggled(bool)), acceptFilters.at(index), SLOT(setSansEpisode(bool) ) );
+ connect(acceptFilters.at(index), SIGNAL(sansEpisodeChanged(bool)), filterSansEpisode, SLOT(setChecked(bool) ) );
+ //minSeason
+ connect(filterMinSeason, SIGNAL(valueChanged(int)), acceptFilters.at(index), SLOT(setMinSeason(int) ) );
+ connect(acceptFilters.at(index), SIGNAL(minSeasonChanged(int)), filterMinSeason, SLOT(setValue(int) ) );
+ //minEpisode
+ connect(filterMinEpisode, SIGNAL(valueChanged(int)), acceptFilters.at(index), SLOT(setMinEpisode(int) ) );
+ connect(acceptFilters.at(index), SIGNAL(minEpisodeChanged(int)), filterMinEpisode, SLOT(setValue(int) ) );
+ //maxSeason
+ connect(filterMaxSeason, SIGNAL(valueChanged(int)), acceptFilters.at(index), SLOT(setMaxSeason(int) ) );
+ connect(acceptFilters.at(index), SIGNAL(maxSeasonChanged(int)), filterMaxSeason, SLOT(setValue(int) ) );
+ //maxEpisode
+ connect(filterMaxEpisode, SIGNAL(valueChanged(int)), acceptFilters.at(index), SLOT(setMaxEpisode(int) ) );
+ connect(acceptFilters.at(index), SIGNAL(maxEpisodeChanged(int)), filterMaxEpisode, SLOT(setValue(int) ) );
+ //matches
+ connect(acceptFilters.at(index), SIGNAL(matchesChanged(const QValueList<FilterMatch>&)), this, SLOT(updateMatches(const QValueList<FilterMatch>&) ) );
+
+ connect(processFilter, SIGNAL(clicked()), acceptFilters.at(index), SIGNAL(rescanFilter()) );
+
+ }
+ else
+ {
+ //title
+ connect(filterTitle, SIGNAL(textChanged(const QString &)), rejectFilters.at(index), SLOT(setTitle(const QString &) ) );
+ connect(rejectFilters.at(index), SIGNAL(titleChanged(const QString &)), this, SLOT(setFilterTitle(const QString &) ) );
+ //active
+ connect(filterActive, SIGNAL(toggled(bool)), rejectFilters.at(index), SLOT(setActive(bool) ) );
+ connect(rejectFilters.at(index), SIGNAL(activeChanged(bool)), filterActive, SLOT(setChecked(bool) ) );
+ //regExps
+ connect(filterRegExps, SIGNAL(changed()), this, SLOT(updateRegExps()) );
+ //series
+ connect(filterSeries, SIGNAL(toggled(bool)), rejectFilters.at(index), SLOT(setSeries(bool) ) );
+ connect(rejectFilters.at(index), SIGNAL(seriesChanged(bool)), filterSeries, SLOT(setChecked(bool) ) );
+ //sansEpisode
+ connect(filterSansEpisode, SIGNAL(toggled(bool)), rejectFilters.at(index), SLOT(setSansEpisode(bool) ) );
+ connect(rejectFilters.at(index), SIGNAL(sansEpisodeChanged(bool)), filterSansEpisode, SLOT(setChecked(bool) ) );
+ //minSeason
+ connect(filterMinSeason, SIGNAL(valueChanged(int)), rejectFilters.at(index), SLOT(setMinSeason(int) ) );
+ connect(rejectFilters.at(index), SIGNAL(minSeasonChanged(int)), filterMinSeason, SLOT(setValue(int) ) );
+ //minEpisode
+ connect(filterMinEpisode, SIGNAL(valueChanged(int)), rejectFilters.at(index), SLOT(setMinEpisode(int) ) );
+ connect(rejectFilters.at(index), SIGNAL(minEpisodeChanged(int)), filterMinEpisode, SLOT(setValue(int) ) );
+ //maxSeason
+ connect(filterMaxSeason, SIGNAL(valueChanged(int)), rejectFilters.at(index), SLOT(setMaxSeason(int) ) );
+ connect(rejectFilters.at(index), SIGNAL(maxSeasonChanged(int)), filterMaxSeason, SLOT(setValue(int) ) );
+ //maxEpisode
+ connect(filterMaxEpisode, SIGNAL(valueChanged(int)), rejectFilters.at(index), SLOT(setMaxEpisode(int) ) );
+ connect(rejectFilters.at(index), SIGNAL(maxEpisodeChanged(int)), filterMaxEpisode, SLOT(setValue(int) ) );
+ //matches
+ connect(rejectFilters.at(index), SIGNAL(matchesChanged(const QValueList<FilterMatch>&)), this, SLOT(updateMatches(const QValueList<FilterMatch>&) ) );
+
+ connect(processFilter, SIGNAL(clicked()), rejectFilters.at(index), SIGNAL(rescanFilter()) );
+
+ }
+ }
+
+ void RssFeedManager::disconnectFilter(int index, bool acceptFilter)
+ {
+ if (acceptFilter)
+ {
+ //title
+ disconnect(filterTitle, SIGNAL(textChanged(const QString &)), acceptFilters.at(index), SLOT(setTitle(const QString &) ) );
+ disconnect(acceptFilters.at(index), SIGNAL(titleChanged(const QString &)), this, SLOT(setFilterTitle(const QString &) ) );
+ //active
+ disconnect(filterActive, SIGNAL(toggled(bool)), acceptFilters.at(index), SLOT(setActive(bool) ) );
+ disconnect(acceptFilters.at(index), SIGNAL(activeChanged(bool)), filterActive, SLOT(setChecked(bool) ) );
+ //regExps
+ disconnect(filterRegExps, SIGNAL(changed()), this, SLOT(updateRegExps()) );
+ //series
+ disconnect(filterSeries, SIGNAL(toggled(bool)), acceptFilters.at(index), SLOT(setSeries(bool) ) );
+ disconnect(acceptFilters.at(index), SIGNAL(seriesChanged(bool)), filterSeries, SLOT(setChecked(bool) ) );
+ //sansEpisode
+ disconnect(filterSansEpisode, SIGNAL(toggled(bool)), acceptFilters.at(index), SLOT(setSansEpisode(bool) ) );
+ disconnect(acceptFilters.at(index), SIGNAL(sansEpisodeChanged(bool)), filterSansEpisode, SLOT(setChecked(bool) ) );
+ //minSeason
+ disconnect(filterMinSeason, SIGNAL(valueChanged(int)), acceptFilters.at(index), SLOT(setMinSeason(int) ) );
+ disconnect(acceptFilters.at(index), SIGNAL(minSeasonChanged(int)), filterMinSeason, SLOT(setValue(int) ) );
+ //minEpisode
+ disconnect(filterMinEpisode, SIGNAL(valueChanged(int)), acceptFilters.at(index), SLOT(setMinEpisode(int) ) );
+ disconnect(acceptFilters.at(index), SIGNAL(minEpisodeChanged(int)), filterMinEpisode, SLOT(setValue(int) ) );
+ //maxSeason
+ disconnect(filterMaxSeason, SIGNAL(valueChanged(int)), acceptFilters.at(index), SLOT(setMaxSeason(int) ) );
+ disconnect(acceptFilters.at(index), SIGNAL(maxSeasonChanged(int)), filterMaxSeason, SLOT(setValue(int) ) );
+ //maxEpisode
+ disconnect(filterMaxEpisode, SIGNAL(valueChanged(int)), acceptFilters.at(index), SLOT(setMaxEpisode(int) ) );
+ disconnect(acceptFilters.at(index), SIGNAL(maxEpisodeChanged(int)), filterMaxEpisode, SLOT(setValue(int) ) );
+ //matches
+ disconnect(acceptFilters.at(index), SIGNAL(matchesChanged(const QValueList<FilterMatch>&)), this, SLOT(updateMatches(const QValueList<FilterMatch>&) ) );
+
+ disconnect(processFilter, SIGNAL(clicked()), acceptFilters.at(index), SIGNAL(rescanFilter()) );
+ }
+ else
+ {
+ //title
+ disconnect(filterTitle, SIGNAL(textChanged(const QString &)), rejectFilters.at(index), SLOT(setTitle(const QString &) ) );
+ disconnect(rejectFilters.at(index), SIGNAL(titleChanged(const QString &)), this, SLOT(setFilterTitle(const QString &) ) );
+ //active
+ disconnect(filterActive, SIGNAL(toggled(bool)), rejectFilters.at(index), SLOT(setActive(bool) ) );
+ disconnect(rejectFilters.at(index), SIGNAL(activeChanged(bool)), filterActive, SLOT(setChecked(bool) ) );
+ //regExps
+ disconnect(filterRegExps, SIGNAL(changed()), this, SLOT(updateRegExps()) );
+ //series
+ disconnect(filterSeries, SIGNAL(toggled(bool)), rejectFilters.at(index), SLOT(setSeries(bool) ) );
+ disconnect(rejectFilters.at(index), SIGNAL(seriesChanged(bool)), filterSeries, SLOT(setChecked(bool) ) );
+ //sansEpisode
+ disconnect(filterSansEpisode, SIGNAL(toggled(bool)), rejectFilters.at(index), SLOT(setSansEpisode(bool) ) );
+ disconnect(rejectFilters.at(index), SIGNAL(sansEpisodeChanged(bool)), filterSansEpisode, SLOT(setChecked(bool) ) );
+ //minSeason
+ disconnect(filterMinSeason, SIGNAL(valueChanged(int)), rejectFilters.at(index), SLOT(setMinSeason(int) ) );
+ disconnect(rejectFilters.at(index), SIGNAL(minSeasonChanged(int)), filterMinSeason, SLOT(setValue(int) ) );
+ //minEpisode
+ disconnect(filterMinEpisode, SIGNAL(valueChanged(int)), rejectFilters.at(index), SLOT(setMinEpisode(int) ) );
+ disconnect(rejectFilters.at(index), SIGNAL(minEpisodeChanged(int)), filterMinEpisode, SLOT(setValue(int) ) );
+ //maxSeason
+ disconnect(filterMaxSeason, SIGNAL(valueChanged(int)), rejectFilters.at(index), SLOT(setMaxSeason(int) ) );
+ disconnect(rejectFilters.at(index), SIGNAL(maxSeasonChanged(int)), filterMaxSeason, SLOT(setValue(int) ) );
+ //maxEpisode
+ disconnect(filterMaxEpisode, SIGNAL(valueChanged(int)), rejectFilters.at(index), SLOT(setMaxEpisode(int) ) );
+ disconnect(rejectFilters.at(index), SIGNAL(maxEpisodeChanged(int)), filterMaxEpisode, SLOT(setValue(int) ) );
+ //matches
+ disconnect(rejectFilters.at(index), SIGNAL(matchesChanged(const QValueList<FilterMatch>&)), this, SLOT(updateMatches(const QValueList<FilterMatch>&) ) );
+
+ disconnect(processFilter, SIGNAL(clicked()), rejectFilters.at(index), SIGNAL(rescanFilter()) );
+
+ }
+ }
+
+ void RssFeedManager::addNewFeed(RssFeed feed)
+ {
+ if (feeds.isEmpty())
+ deleteFeed->setEnabled(true);
+
+ feeds.append(new RssFeed(feed));
+
+ int index = feeds.count()-1;
+ feedlist->insertItem(feeds.at(index)->title());
+ feedlist->setCurrentItem(index);
+
+ //update the feed list
+ connect(feeds.at(index), SIGNAL(titleChanged(const QString&)), this, SLOT(updateFeedList()) );
+
+ //clear the articles list when the url is changed
+ connect(feeds.at(index), SIGNAL(feedUrlChanged(const KURL&)), this, SLOT(clearArticles() ) );
+
+ //connect the scanArticle signal to the scanArticle slot
+ connect(feeds.at(index), SIGNAL(scanRssArticle(RssArticle)), this, SLOT(scanArticle(RssArticle) ) );
+
+ //connect all the fields to the save slot
+ //title
+ connect(feeds.at(index), SIGNAL(titleChanged(const QString &)), this, SLOT(saveFeedList() ) );
+ //url
+ connect(feeds.at(index), SIGNAL(feedUrlChanged(const KURL&)), this, SLOT(saveFeedList() ) );
+ //articleAge
+ connect(feeds.at(index), SIGNAL(articleAgeChanged(int)), this, SLOT(saveFeedList() ) );
+ //active
+ connect(feeds.at(index), SIGNAL(activeChanged(bool)), this, SLOT(saveFeedList() ) );
+ //autoRefresh
+ connect(feeds.at(index), SIGNAL(autoRefreshChanged(const QTime&)), this, SLOT(saveFeedList() ) );
+ //ignoreTTL
+ connect(feeds.at(index), SIGNAL(ignoreTTLChanged(bool)), this, SLOT(saveFeedList() ) );
+
+ }
+
+ void RssFeedManager::addNewAcceptFilter(RssFilter filter)
+ {
+ if (acceptFilters.isEmpty())
+ deleteAcceptFilter->setEnabled(true);
+
+ acceptFilters.append(new RssFilter(filter));
+
+ int index = acceptFilters.count()-1;
+ acceptFilterList->insertItem(acceptFilters.at(index)->title());
+ acceptFilterList->setCurrentItem(index);
+
+ connect(acceptFilters.at(index), SIGNAL(titleChanged(const QString&)), this, SLOT(updateAcceptFilterList()) );
+
+ //connect all the fields to the save slot
+ //title
+ connect(acceptFilters.at(index), SIGNAL(titleChanged(const QString &)), this, SLOT(saveFilterList() ) );
+ //active
+ connect(acceptFilters.at(index), SIGNAL(activeChanged( bool )), this, SLOT(saveFilterList() ) );
+ //regexps
+ connect(acceptFilters.at(index), SIGNAL(regExpsChanged( const QStringList& )), this, SLOT(saveFilterList() ) );
+ //series
+ connect(acceptFilters.at(index), SIGNAL(seriesChanged( bool )), this, SLOT(saveFilterList() ) );
+ //sansEpisode
+ connect(acceptFilters.at(index), SIGNAL(sansEpisodeChanged( bool )), this, SLOT(saveFilterList() ) );
+ //minSeason
+ connect(acceptFilters.at(index), SIGNAL(minSeasonChanged (int )), this, SLOT(saveFilterList() ) );
+ //minEpisode
+ connect(acceptFilters.at(index), SIGNAL(minEpisodeChanged (int )), this, SLOT(saveFilterList() ) );
+ //maxSeason
+ connect(acceptFilters.at(index), SIGNAL(maxSeasonChanged (int )), this, SLOT(saveFilterList() ) );
+ //maxEpiosde
+ connect(acceptFilters.at(index), SIGNAL(maxEpisodeChanged (int )), this, SLOT(saveFilterList() ) );
+ //matches
+ connect(acceptFilters.at(index), SIGNAL(matchesChanged( const QValueList<FilterMatch>& )), this, SLOT(saveFilterList() ) );
+
+ //connect the rescan signal to the rescan slot
+ connect(acceptFilters.at(index), SIGNAL(rescanFilter()), this, SLOT(rescanFilter()) );
+
+// //connect all except the matchesChanged to the rescanFilter slot
+// //title
+// connect(acceptFilters.at(index), SIGNAL(titleChanged(const QString &)), this, SLOT(rescanFilter() ) );
+// //active
+// connect(acceptFilters.at(index), SIGNAL(activeChanged( bool )), this, SLOT(rescanFilter() ) );
+// //regexps
+// connect(acceptFilters.at(index), SIGNAL(regExpsChanged( const QStringList& )), this, SLOT(rescanFilter() ) );
+// //series
+// connect(acceptFilters.at(index), SIGNAL(seriesChanged( bool )), this, SLOT(rescanFilter() ) );
+// //sansEpisode
+// connect(acceptFilters.at(index), SIGNAL(sansEpisodeChanged( bool )), this, SLOT(rescanFilter() ) );
+// //minSeason
+// connect(acceptFilters.at(index), SIGNAL(minSeasonChanged (int )), this, SLOT(rescanFilter() ) );
+// //minEpisode
+// connect(acceptFilters.at(index), SIGNAL(minEpisodeChanged (int )), this, SLOT(rescanFilter() ) );
+// //maxSeason
+// connect(acceptFilters.at(index), SIGNAL(maxSeasonChanged (int )), this, SLOT(rescanFilter() ) );
+// //maxEpiosde
+// connect(acceptFilters.at(index), SIGNAL(maxEpisodeChanged (int )), this, SLOT(rescanFilter() ) );
+
+ }
+
+ void RssFeedManager::addNewRejectFilter(RssFilter filter)
+ {
+ if (rejectFilters.isEmpty())
+ deleteRejectFilter->setEnabled(true);
+
+ rejectFilters.append(new RssFilter(filter));
+
+ int index = rejectFilters.count()-1;
+ rejectFilterList->insertItem(rejectFilters.at(index)->title());
+ rejectFilterList->setCurrentItem(index);
+
+ connect(rejectFilters.at(index), SIGNAL(titleChanged(const QString&)), this, SLOT(updateRejectFilterList()) );
+
+ //connect all the fields to the save slot
+ //title
+ connect(rejectFilters.at(index), SIGNAL(titleChanged(const QString &)), this, SLOT(saveFilterList() ) );
+ //active
+ connect(rejectFilters.at(index), SIGNAL(activeChanged( bool )), this, SLOT(saveFilterList() ) );
+ //regexps
+ connect(rejectFilters.at(index), SIGNAL(regExpsChanged( const QStringList& )), this, SLOT(saveFilterList() ) );
+ //series
+ connect(rejectFilters.at(index), SIGNAL(seriesChanged( bool )), this, SLOT(saveFilterList() ) );
+ //sansEpisode
+ connect(rejectFilters.at(index), SIGNAL(sansEpisodeChanged( bool )), this, SLOT(saveFilterList() ) );
+ //minSeason
+ connect(rejectFilters.at(index), SIGNAL(minSeasonChanged (int )), this, SLOT(saveFilterList() ) );
+ //minEpisode
+ connect(rejectFilters.at(index), SIGNAL(minEpisodeChanged (int )), this, SLOT(saveFilterList() ) );
+ //maxSeason
+ connect(rejectFilters.at(index), SIGNAL(maxSeasonChanged (int )), this, SLOT(saveFilterList() ) );
+ //maxEpiosde
+ connect(rejectFilters.at(index), SIGNAL(maxEpisodeChanged (int )), this, SLOT(saveFilterList() ) );
+ //matches
+ connect(rejectFilters.at(index), SIGNAL(matchesChanged( const QValueList<FilterMatch>& )), this, SLOT(saveFilterList() ) );
+
+ }
+
+ void RssFeedManager::deleteSelectedFeed()
+ {
+ int currentItem = feedlist->currentItem();
+
+ if (currentItem < 0)
+ return;
+
+ int newItem=currentItem-1;
+
+ if (currentItem == -1 && feeds.count())
+ newItem = 0;
+
+ disconnectFeed(currentItem);
+ currentFeed = -1;
+
+ delete feeds.at(currentItem);
+ feeds.remove(currentItem);
+ feedlist->removeItem(currentItem);
+
+ if (feeds.isEmpty())
+ deleteFeed->setEnabled(false);
+
+ if (newItem >= 0)
+ {
+ feedlist->setSelected(newItem, true);
+ }
+
+ saveFeedList();
+ }
+
+ void RssFeedManager::deleteSelectedAcceptFilter()
+ {
+ int currentItem = acceptFilterList->currentItem();
+
+ if (currentItem < 0)
+ return;
+
+ int newItem=currentItem-1;
+
+ if (currentItem == -1 && acceptFilters.count())
+ newItem = 0;
+
+ disconnectFilter(currentItem, true);
+ currentAcceptFilter = -1;
+
+ delete acceptFilters.at(currentItem);
+ acceptFilters.remove(currentItem);
+ acceptFilterList->removeItem(currentItem);
+
+ if (acceptFilters.isEmpty())
+ deleteAcceptFilter->setEnabled(false);
+
+ if (newItem >= 0)
+ {
+ acceptFilterList->setSelected(newItem, true);
+ }
+
+ saveFilterList();
+ }
+
+ void RssFeedManager::deleteSelectedRejectFilter()
+ {
+ int currentItem = rejectFilterList->currentItem();
+
+ if (currentItem < 0)
+ return;
+
+ int newItem=currentItem-1;
+
+ if (currentItem == -1 && rejectFilters.count())
+ newItem = 0;
+
+ disconnectFilter(currentItem, false);
+ currentRejectFilter = -1;
+
+ delete rejectFilters.at(currentItem);
+ rejectFilters.remove(currentItem);
+ rejectFilterList->removeItem(currentItem);
+
+ if (rejectFilters.isEmpty())
+ deleteRejectFilter->setEnabled(false);
+
+ if (newItem >= 0)
+ {
+ rejectFilterList->setSelected(newItem, true);
+ }
+
+ saveFilterList();
+ }
+
+ void RssFeedManager::updateRegExps()
+ {
+ if (currentRejectFilter < 0)
+ {
+ //accept filter is active
+ acceptFilters.at(currentAcceptFilter)->setRegExps(filterRegExps->items());
+ }
+ else
+ {
+ //reject filter is active
+ rejectFilters.at(currentRejectFilter)->setRegExps(filterRegExps->items());
+ }
+ }
+
+ void RssFeedManager::updateFeedList(int item)
+ {
+ int cursorPos = feedTitle->cursorPosition();
+ if (item < 0)
+ {
+ //let's check which one sent the signal - if we can't figure it all then update them all
+ int pos = feeds.find((RssFeed *)sender());
+
+ if (pos < 0)
+ {
+ for (int i=0; i<feedlist->count(); i++)
+ {
+ feedlist->changeItem(feeds.at(i)->title(), i);
+ }
+ }
+ else
+ {
+ //just change the feed sending the signal
+ feedlist->changeItem(feeds.at(pos)->title(), pos);
+ if (feedlist->isSelected(pos))
+ {
+ feedTitle->setFocus();
+ }
+ }
+ }
+ else
+ {
+ //just update item
+ feedlist->changeItem(feeds.at(item)->title(), item);
+ }
+ feedTitle->setCursorPosition(cursorPos);
+ }
+
+ void RssFeedManager::updateAcceptFilterList(int item)
+ {
+ int cursorPos = filterTitle->cursorPosition();
+ if (item < 0)
+ {
+ //let's check which one sent the signal - if we can't figure it all then update them all
+ int pos = acceptFilters.find((RssFilter *)sender());
+
+ if (pos < 0)
+ {
+ for (int i=0; i<feedlist->count(); i++)
+ {
+ acceptFilterList->changeItem(acceptFilters.at(i)->title(), i);
+ }
+ }
+ else
+ {
+ //just change the feed sending the signal
+ acceptFilterList->changeItem(acceptFilters.at(pos)->title(), pos);
+ if (acceptFilterList->isSelected(pos))
+ {
+ filterTitle->setFocus();
+ }
+ }
+ }
+ else
+ {
+ //just update item
+ acceptFilterList->changeItem(acceptFilters.at(item)->title(), item);
+ }
+ filterTitle->setCursorPosition(cursorPos);
+ }
+
+ void RssFeedManager::updateRejectFilterList(int item)
+ {
+ int cursorPos = filterTitle->cursorPosition();
+ if (item < 0)
+ {
+ //let's check which one sent the signal - if we can't figure it all then update them all
+ int pos = rejectFilters.find((RssFilter *)sender());
+
+ if (pos < 0)
+ {
+ for (int i=0; i<feedlist->count(); i++)
+ {
+ rejectFilterList->changeItem(rejectFilters.at(i)->title(), i);
+ }
+ }
+ else
+ {
+ //just change the feed sending the signal
+ rejectFilterList->changeItem(rejectFilters.at(pos)->title(), pos);
+ if (rejectFilterList->isSelected(pos))
+ {
+ filterTitle->setFocus();
+ }
+ }
+ }
+ else
+ {
+ //just update item
+ rejectFilterList->changeItem(rejectFilters.at(item)->title(), item);
+ }
+ filterTitle->setCursorPosition(cursorPos);
+ }
+
+ void RssFeedManager::updateArticles(const RssArticle::List& articles)
+ {
+ feedArticles->setNumRows(articles.count());
+ for (int i=0; i<articles.count(); i++)
+ {
+ QString info;
+ if (articles[i].downloaded()==1)
+ {
+ info = ": Manually downloaded";
+ }
+ else if (articles[i].downloaded()==3)
+ {
+ info = ": Automatically downloaded";
+ }
+ feedArticles->setText(i, 0, articles[i].title() + info);
+ feedArticles->setText(i, 1, articles[i].description());
+ feedArticles->setText(i, 2, articles[i].link().prettyURL());
+ }
+ }
+
+ void RssFeedManager::updateMatches(const QValueList<FilterMatch>& matches)
+ {
+ filterMatches->setNumRows(matches.count());
+ for (int i=0; i<matches.count(); i++)
+ {
+ filterMatches->setText(i, 0, QString::number(matches[i].season()));
+ filterMatches->setText(i, 1, QString::number(matches[i].episode()));
+ filterMatches->setText(i, 2, matches[i].time());
+ filterMatches->setText(i, 3, matches[i].link());
+ }
+
+ changedMatchSelection();
+ }
+
+ void RssFeedManager::changedArticleSelection()
+ {
+ bool downloadEnabled = false;
+ for (int i=0; i<feedArticles->numSelections(); i++)
+ {
+ if (feedArticles->selection(i).numRows())
+ {
+ downloadEnabled = true;
+ break;
+ }
+ }
+ downloadArticle->setEnabled(downloadEnabled);
+ }
+
+ void RssFeedManager::changedMatchSelection()
+ {
+ bool downloadEnabled = false;
+ for (int i=0; i<filterMatches->numSelections(); i++)
+ {
+ if (filterMatches->selection(i).numRows())
+ {
+ downloadEnabled = true;
+ break;
+ }
+ }
+ downloadFilterMatch->setEnabled(downloadEnabled);
+ deleteFilterMatch->setEnabled(downloadEnabled);
+ }
+
+ void RssFeedManager::downloadSelectedArticles()
+ {
+ for (int i=0; i<feedArticles->numSelections(); i++)
+ {
+ int endRow = feedArticles->selection(i).topRow() + feedArticles->selection(i).numRows();
+ RssLinkDownloader * curDownload;
+ for (int j=feedArticles->selection(i).topRow(); j<endRow; j++)
+ {
+ curDownload = new RssLinkDownloader(m_core, feedArticles->text(j, 2));
+ for (int i=0; i<feeds.count(); i++)
+ {
+ connect(curDownload, SIGNAL(linkDownloaded( QString, int )), feeds.at(i), SLOT(setDownloaded(QString, int)) );
+ }
+ }
+ }
+ }
+
+ void RssFeedManager::downloadSelectedMatches()
+ {
+ for (int i=0; i<filterMatches->numSelections(); i++)
+ {
+ int endRow = filterMatches->selection(i).topRow() + filterMatches->selection(i).numRows();
+ for (int j=filterMatches->selection(i).topRow(); j<endRow; j++)
+ {
+ new RssLinkDownloader(m_core, filterMatches->text(j, 3));
+ }
+ }
+ }
+
+ void RssFeedManager::deleteSelectedMatches()
+ {
+ QStringList selectedLinks;
+ for (int i=0; i<filterMatches->numSelections(); i++)
+ {
+ int endRow = filterMatches->selection(i).topRow() + filterMatches->selection(i).numRows();
+ for (int j=filterMatches->selection(i).topRow(); j<endRow; j++)
+ {
+ //add a match to the list of selected matches
+ selectedLinks.append(filterMatches->text(j, 3));
+ }
+ }
+
+ RssFilter * curFilter;
+ if (currentRejectFilter<0)
+ {
+ //we're currently testing an acceptFilter
+ curFilter = acceptFilters.at(currentAcceptFilter);
+ }
+ else
+ {
+ //it's a reject filter
+ curFilter = rejectFilters.at(currentRejectFilter);
+ }
+
+ for (int i=0; i<selectedLinks.count(); i++)
+ {
+ curFilter->deleteMatch( selectedLinks[i] );
+ }
+
+ updateMatches(curFilter->matches());
+ }
+
+ void RssFeedManager::changedActiveFeed()
+ {
+ if (currentFeed != feedlist->currentItem() || currentFeed < 0)
+ {
+ //the selection has indeed changed
+ if (currentFeed >= 0)
+ {
+ //disconnect the gui signals from the old feed
+ disconnectFeed(currentFeed);
+ }
+
+ //update the currentFeed
+ currentFeed = feedlist->currentItem();
+ if (currentFeed >= 0)
+ {
+ //set the values
+ //title
+ feedTitle->setText(feeds.at(currentFeed)->title());
+ //url
+ feedUrl->setKURL(feeds.at(currentFeed)->feedUrl());
+ refreshFeed->setEnabled(!feeds.at(currentFeed)->feedUrl().url().isEmpty());
+ //articleAge
+ feedArticleAge->setValue(feeds.at(currentFeed)->articleAge());
+ //active
+ feedActive->setChecked(feeds.at(currentFeed)->active());
+ //autoRefresh
+ feedAutoRefresh->setTime(feeds.at(currentFeed)->autoRefresh());
+ //ignoreTTL
+ feedIgnoreTTL->setChecked(feeds.at(currentFeed)->ignoreTTL());
+ feedAutoRefresh->setEnabled(feeds.at(currentFeed)->ignoreTTL());
+ //articles
+ updateArticles(feeds.at(currentFeed)->articles());
+
+ //title
+ feedTitle->setEnabled(true);
+ //url
+ feedUrl->setEnabled(true);
+ //articleAge
+ feedArticleAge->setEnabled(true);
+ //active
+ feedActive->setEnabled(true);
+ //ignoreTTL
+ feedIgnoreTTL->setEnabled(true);
+
+ //connect all the signals
+ connectFeed(currentFeed);
+ }
+ else
+ {
+ //clear the items
+ //title
+ feedTitle->clear();
+ //url
+ feedUrl->clear();
+ //articleAge
+ feedArticleAge->setValue(0);
+ //active
+ feedActive->setChecked(false);
+ //autoRefresh
+ feedAutoRefresh->setTime(QTime());
+ //ignoreTTL
+ feedIgnoreTTL->setChecked(false);
+ //articles
+ feedArticles->setNumRows(0);
+
+ //title
+ feedTitle->setEnabled(false);
+ //url
+ feedUrl->setEnabled(false);
+ //articleAge
+ feedArticleAge->setEnabled(false);
+ //active
+ feedActive->setEnabled(false);
+ //autoRefresh
+ feedAutoRefresh->setEnabled(false);
+ //ignoreTTL
+ feedIgnoreTTL->setEnabled(false);
+ }
+ }
+ }
+
+ void RssFeedManager::changedActiveAcceptFilter()
+ {
+ if (currentRejectFilter >= 0)
+ {
+ rejectFilterList->setSelected(currentRejectFilter, false);
+ disconnectFilter(currentRejectFilter, false);
+ currentRejectFilter = -1;
+ }
+
+ if (currentAcceptFilter != acceptFilterList->currentItem() || currentAcceptFilter < 0)
+ {
+ //the selection has indeed changed
+
+ if (currentAcceptFilter >= 0)
+ {
+ //disconnect the gui signals from the old feed
+ disconnectFilter(currentAcceptFilter, true);
+
+ }
+
+ //update the currentFeed
+ currentAcceptFilter = acceptFilterList->currentItem();
+
+ if (currentAcceptFilter >= 0)
+ {
+ //set the values
+ filterTitle->setText(acceptFilters.at(currentAcceptFilter)->title());
+ filterActive->setChecked(acceptFilters.at(currentAcceptFilter)->active());
+ filterRegExps->setItems(acceptFilters.at(currentAcceptFilter)->regExps());
+ filterSeries->setChecked(acceptFilters.at(currentAcceptFilter)->series());
+ filterSansEpisode->setChecked(acceptFilters.at(currentAcceptFilter)->sansEpisode());
+ filterMinSeason->setValue(acceptFilters.at(currentAcceptFilter)->minSeason());
+ filterMinEpisode->setValue(acceptFilters.at(currentAcceptFilter)->minEpisode());
+ filterMaxSeason->setValue(acceptFilters.at(currentAcceptFilter)->maxSeason());
+ filterMaxEpisode->setValue(acceptFilters.at(currentAcceptFilter)->maxEpisode());
+
+ updateMatches(acceptFilters.at(currentAcceptFilter)->matches());
+
+ filterTitle->setEnabled(true);
+ filterActive->setEnabled(true);
+ filterRegExps->setEnabled(true);
+ filterSeries->setEnabled(true);
+ filterSansEpisode->setEnabled(true);
+ filterMinSeason->setEnabled(true);
+ filterMinEpisode->setEnabled(true);
+ filterMaxSeason->setEnabled(true);
+ filterMaxEpisode->setEnabled(true);
+
+ processFilter->setEnabled(true);
+ testText->setEnabled(true);
+
+ //connect all the signals
+ connectFilter(currentAcceptFilter, true);
+ }
+ else
+ {
+ if (currentRejectFilter < 0)
+ {
+ //clear the items
+ filterTitle->clear();
+ filterActive->setChecked(false);
+ filterRegExps->clear();
+ filterSeries->setChecked(false);
+ filterSansEpisode->setChecked(false);
+ filterMinSeason->setValue(0);
+ filterMinEpisode->setValue(0);
+ filterMaxSeason->setValue(0);
+ filterMaxEpisode->setValue(0);
+ filterMatches->setNumRows(0);
+
+ filterTitle->setEnabled(false);
+ filterActive->setEnabled(false);
+ filterRegExps->setEnabled(false);
+ filterSeries->setEnabled(false);
+ filterSansEpisode->setEnabled(false);
+ filterMinSeason->setEnabled(false);
+ filterMinEpisode->setEnabled(false);
+ filterMaxSeason->setEnabled(false);
+ filterMaxEpisode->setEnabled(false);
+
+ processFilter->setEnabled(false);
+ testText->setEnabled(false);
+
+ }
+ }
+ }
+ }
+
+ void RssFeedManager::changedActiveRejectFilter()
+ {
+ if (currentAcceptFilter >= 0)
+ {
+ acceptFilterList->setSelected(currentAcceptFilter, false);
+ disconnectFilter(currentAcceptFilter, true);
+ currentAcceptFilter = -1;
+ }
+
+ if (currentRejectFilter != rejectFilterList->currentItem() || currentRejectFilter < 0)
+ {
+ //the selection has indeed changed
+
+ if (currentRejectFilter >= 0)
+ {
+ //disconnect the gui signals from the old feed
+ disconnectFilter(currentRejectFilter, false);
+ }
+
+ //update the currentFeed
+ currentRejectFilter = rejectFilterList->currentItem();
+
+ if (currentRejectFilter >= 0)
+ {
+ //set the values
+ //title
+ filterTitle->setText(rejectFilters.at(currentRejectFilter)->title());
+ filterActive->setChecked(rejectFilters.at(currentRejectFilter)->active());
+ filterRegExps->setItems(rejectFilters.at(currentRejectFilter)->regExps());
+ filterSeries->setChecked(rejectFilters.at(currentRejectFilter)->series());
+ filterSansEpisode->setChecked(rejectFilters.at(currentRejectFilter)->sansEpisode());
+ filterMinSeason->setValue(rejectFilters.at(currentRejectFilter)->minSeason());
+ filterMinEpisode->setValue(rejectFilters.at(currentRejectFilter)->minEpisode());
+ filterMaxSeason->setValue(rejectFilters.at(currentRejectFilter)->maxSeason());
+ filterMaxEpisode->setValue(rejectFilters.at(currentRejectFilter)->maxEpisode());
+
+ updateMatches(rejectFilters.at(currentRejectFilter)->matches());
+
+ filterTitle->setEnabled(true);
+ filterActive->setEnabled(true);
+ filterRegExps->setEnabled(true);
+ filterSeries->setEnabled(true);
+ filterSansEpisode->setEnabled(true);
+ filterMinSeason->setEnabled(true);
+ filterMinEpisode->setEnabled(true);
+ filterMaxSeason->setEnabled(true);
+ filterMaxEpisode->setEnabled(true);
+
+ processFilter->setEnabled(true);
+ testText->setEnabled(true);
+
+ //connect all the signals
+ connectFilter(currentRejectFilter, false);
+ }
+ else
+ {
+ if (currentRejectFilter < 0)
+ {
+ //clear the items
+ filterTitle->clear();
+ filterActive->setChecked(false);
+ filterRegExps->clear();
+ filterSeries->setChecked(false);
+ filterSansEpisode->setChecked(false);
+ filterMinSeason->setValue(0);
+ filterMinEpisode->setValue(0);
+ filterMaxSeason->setValue(0);
+ filterMaxEpisode->setValue(0);
+ filterMatches->setNumRows(0);
+
+ filterTitle->setEnabled(false);
+ filterActive->setEnabled(false);
+ filterRegExps->setEnabled(false);
+ filterSeries->setEnabled(false);
+ filterSansEpisode->setEnabled(false);
+ filterMinSeason->setEnabled(false);
+ filterMinEpisode->setEnabled(false);
+ filterMaxSeason->setEnabled(false);
+ filterMaxEpisode->setEnabled(false);
+
+ processFilter->setEnabled(false);
+ testText->setEnabled(false);
+ }
+ }
+ }
+ }
+
+ QString RssFeedManager::getFeedListFilename()
+ {
+ return KGlobal::dirs()->saveLocation("data","ktorrent") + "rssfeeds.ktr";
+ }
+
+ QString RssFeedManager::getFilterListFilename()
+ {
+ return KGlobal::dirs()->saveLocation("data","ktorrent") + "rssfilters.ktr";
+ }
+
+ void RssFeedManager::saveFeedList()
+ {
+ if (feedListSaving)
+ return;
+
+ feedListSaving = true;
+
+ QString filename = getFeedListFilename();
+
+ //save feeds to disk
+ QFile file(filename);
+
+ file.open( IO_WriteOnly );
+ QDataStream out(&file);
+
+ out << feeds.count();
+
+ for (int i=0; i<feeds.count(); i++)
+ {
+ out << *(feeds.at(i));
+ }
+
+ feedListSaving = false;
+ }
+
+ void RssFeedManager::loadFeedList()
+ {
+ QString filename = getFeedListFilename();
+
+ //load feeds from disk
+ QFile file(filename);
+
+ if (file.exists())
+ {
+ file.open( IO_ReadOnly );
+ QDataStream in(&file);
+
+ int numFeeds;
+
+ in >> numFeeds;
+
+ RssFeed curFeed;
+
+ for (int i=0; i<numFeeds; i++)
+ {
+ in >> curFeed;
+ addNewFeed(curFeed);
+ }
+
+ changedActiveFeed();
+ }
+ }
+
+ void RssFeedManager::saveFilterList()
+ {
+ if (filterListSaving)
+ return;
+
+ filterListSaving = true;
+
+ QString filename = getFilterListFilename();
+
+ //save feeds to disk
+ QFile file(filename);
+
+ file.open( IO_WriteOnly );
+ QDataStream out(&file);
+
+ out << acceptFilters.count();
+
+ for (int i=0; i<acceptFilters.count(); i++)
+ {
+ out << *(acceptFilters.at(i));
+ }
+
+ out << rejectFilters.count();
+
+ for (int i=0; i<rejectFilters.count(); i++)
+ {
+ out << *(rejectFilters.at(i));
+ }
+
+ filterListSaving = false;
+ }
+
+ void RssFeedManager::loadFilterList()
+ {
+ QString filename = getFilterListFilename();
+
+ //load feeds from disk
+ QFile file(filename);
+
+ if (file.exists())
+ {
+ file.open( IO_ReadOnly );
+ QDataStream in(&file);
+
+ int numFilters;
+
+ in >> numFilters;
+
+ RssFilter curFilter;
+
+ for (int i=0; i<numFilters; i++)
+ {
+ in >> curFilter;
+ addNewAcceptFilter(curFilter);
+ }
+
+ in >> numFilters;
+
+ for (int i=0; i<numFilters; i++)
+ {
+ in >> curFilter;
+ addNewRejectFilter(curFilter);
+ }
+
+ //go through and grab the reject filters
+ changedActiveRejectFilter();
+ changedActiveAcceptFilter();
+ }
+ }
+
+ void RssFeedManager::scanArticle(RssArticle article, RssFilter * filter)
+ {
+ //first run it through the reject filters - if any match go no further
+ for (int i=0; i<rejectFilters.count(); i++)
+ {
+ if (rejectFilters.at(i)->scanArticle(article, false))
+ {
+ return;
+ }
+ }
+
+ if (filter)
+ {
+ //we were passed a filter - so just scan it with that one
+ if (filter->scanArticle(article))
+ {
+ RssLinkDownloader * curDownload = new RssLinkDownloader(m_core, article.link().prettyURL(), filter);
+ for (int i=0; i<feeds.count(); i++)
+ {
+ connect(curDownload, SIGNAL(linkDownloaded( QString, int )), feeds.at(i), SLOT(setDownloaded(QString, int)) );
+ }
+ }
+ }
+ else
+ {
+ for (int i=0; i<acceptFilters.count(); i++)
+ {
+ if (acceptFilters.at(i)->scanArticle(article))
+ {
+ RssLinkDownloader * curDownload = new RssLinkDownloader(m_core, article.link().prettyURL(), acceptFilters.at(i));
+ for (int i=0; i<feeds.count(); i++)
+ {
+ connect(curDownload, SIGNAL(linkDownloaded( QString, int )), feeds.at(i), SLOT(setDownloaded(QString, int)) );
+ }
+ }
+ }
+ }
+ }
+
+ void RssFeedManager::rescanFilter()
+ {
+ int pos = acceptFilters.find((RssFilter *)sender());
+
+ if (pos >= 0)
+ {
+ for (int i=0; i<feeds.count(); i++)
+ {
+ for (int j=0; j<feeds.at(i)->articles().count(); j++)
+ {
+ scanArticle(feeds.at(i)->articles()[j], (RssFilter *)sender());
+ }
+ }
+ }
+ }
+
+ void RssFeedManager::testTextChanged()
+ {
+ testText->setPaletteBackgroundColor(QColor(255, 255, 255));
+ testTestText->setEnabled(!testText->text().isEmpty());
+ }
+
+ void RssFeedManager::testFilter()
+ {
+ RssFilter * curFilter;
+ if (currentRejectFilter<0)
+ {
+ //we're currently testing an acceptFilter
+ curFilter = acceptFilters.at(currentAcceptFilter);
+ }
+ else
+ {
+ //it's a reject filter
+ curFilter = rejectFilters.at(currentRejectFilter);
+ }
+
+ RssArticle testArticle;
+ testArticle.setTitle(testText->text());
+
+ if (curFilter->scanArticle(testArticle, false, false))
+ {
+ testText->setPaletteBackgroundColor(QColor(0, 255, 0));
+ }
+ else
+ {
+ testText->setPaletteBackgroundColor(QColor(255, 0, 0));
+ }
+ }
+
+ void RssFeedManager::setFilterTitle(const QString& title)
+ {
+ int cursorPos = filterTitle->cursorPosition();
+ filterTitle->setText(title);
+ filterTitle->setCursorPosition(cursorPos);
+ }
+
+ void RssFeedManager::setFeedTitle(const QString& title)
+ {
+ int cursorPos = feedTitle->cursorPosition();
+ feedTitle->setText(title);
+ feedTitle->setCursorPosition(cursorPos);
+ }
+
+}
diff --git a/plugins/rssfeed/rssfeedmanager.h b/plugins/rssfeed/rssfeedmanager.h
new file mode 100644
index 0000000..1afc766
--- /dev/null
+++ b/plugins/rssfeed/rssfeedmanager.h
@@ -0,0 +1,130 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef RSSFEEDMANAGER_H
+#define RSSFEEDMANAGER_H
+
+#include <kdirlister.h>
+#include <kfileitem.h>
+#include <qstring.h>
+#include <qobject.h>
+#include <qdir.h>
+
+#include <qptrlist.h>
+#include <qwidget.h>
+#include "rssfeedwidget.h"
+
+#include "rssfeed.h"
+#include "rssfilter.h"
+
+namespace kt
+{
+
+ class CoreInterface;
+
+ /**
+ * @brief RssFeed Manager Class
+ * @author Alan Jones <skyphyr@gmail.com>
+ *
+ *
+ */
+ class RssFeedManager : public RssFeedWidget
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Default constructor.
+ * @param core Pointer to core interface
+ * @param openSilently Wheather to open torrent silently or nor.
+ */
+ RssFeedManager(CoreInterface* core, QWidget * parent = 0);
+ ~RssFeedManager();
+
+ public slots:
+ void changedActiveFeed();
+ void changedArticleSelection();
+ void changedFeedUrl();
+ void changedMatchSelection();
+ void updateArticles(const RssArticle::List& articles);
+ void downloadSelectedArticles();
+ void downloadSelectedMatches();
+ void deleteSelectedMatches();
+
+ void changedActiveAcceptFilter();
+ void changedActiveRejectFilter();
+
+ void clearArticles();
+
+ void updateFeedList(int item=-1);
+ void addNewFeed(RssFeed feed = RssFeed());
+ void deleteSelectedFeed();
+
+ void updateAcceptFilterList(int item=-1);
+ void addNewAcceptFilter(RssFilter filter = RssFilter());
+ void deleteSelectedAcceptFilter();
+
+ void updateRejectFilterList(int item=-1);
+ void addNewRejectFilter(RssFilter filter = RssFilter());
+ void deleteSelectedRejectFilter();
+
+ void updateRegExps();
+ void updateMatches(const QValueList<FilterMatch>& matches);
+
+ void saveFeedList();
+ void saveFilterList();
+
+ void disconnectFeed(int index);
+ void connectFeed(int index);
+
+ void disconnectFilter(int index, bool acceptFilter);
+ void connectFilter(int index, bool acceptFilter);
+
+ void scanArticle(RssArticle article, RssFilter * filter = NULL);
+ void rescanFilter();
+
+ void testTextChanged();
+ void testFilter();
+
+ void setFilterTitle(const QString& title);
+ void setFeedTitle(const QString& title);
+
+ private:
+ CoreInterface* m_core;
+
+ QPtrList<RssFeed> feeds;
+ int currentFeed;
+
+ QPtrList<RssFilter> acceptFilters;
+ int currentAcceptFilter;
+ QPtrList<RssFilter> rejectFilters;
+ int currentRejectFilter;
+
+ QString getFeedListFilename();
+ void loadFeedList();
+
+ QString getFilterListFilename();
+ void loadFilterList();
+
+ bool feedListSaving;
+ bool filterListSaving;
+
+ };
+}
+#endif
diff --git a/plugins/rssfeed/rssfeedplugin.cpp b/plugins/rssfeed/rssfeedplugin.cpp
new file mode 100644
index 0000000..0d845b7
--- /dev/null
+++ b/plugins/rssfeed/rssfeedplugin.cpp
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+#include <kiconloader.h>
+
+#include <interfaces/coreinterface.h>
+#include <interfaces/guiinterface.h>
+#include <interfaces/plugin.h>
+#include <util/constants.h>
+#include <util/log.h>
+
+#include <qstring.h>
+#include <qfile.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kglobal.h>
+
+#include "rssfeedmanager.h"
+#include "rssfeedplugin.h"
+
+using namespace bt;
+
+K_EXPORT_COMPONENT_FACTORY(ktrssfeedplugin,KGenericFactory<kt::RssFeedPlugin>("rssfeedplugin"))
+
+namespace kt
+{
+ const QString NAME = "RSS Feeds";
+ const QString AUTHOR = "Alan Jones";
+ const QString EMAIL = "skyphyr@gmail.com";
+ const QString DESCRIPTION = i18n("Automatically scans RSS feeds for torrent matching regular expressions and loads them.");
+
+ RssFeedPlugin::RssFeedPlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name, args,NAME,i18n("RSS Feeds"),AUTHOR,EMAIL,DESCRIPTION,"player_playlist")
+ {
+ m_rssFeedManager = 0;
+ }
+
+
+ RssFeedPlugin::~RssFeedPlugin()
+ {
+ }
+
+ void RssFeedPlugin::load()
+ {
+ //add the new tab to the gui
+ KIconLoader* iload = KGlobal::iconLoader();
+ m_rssFeedManager = new RssFeedManager(getCore());
+ getGUI()->addTabPage(
+ m_rssFeedManager,iload->loadIconSet("player_playlist", KIcon::Small),
+ i18n("RSS Feeds"));
+
+ }
+
+ void RssFeedPlugin::unload()
+ {
+ // be sure to remove the page's tab before deleting the widget
+ getGUI()->removeTabPage(m_rssFeedManager);
+ delete m_rssFeedManager;
+ m_rssFeedManager = 0;
+ }
+
+ bool RssFeedPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+
+}
+
diff --git a/plugins/rssfeed/rssfeedplugin.h b/plugins/rssfeed/rssfeedplugin.h
new file mode 100644
index 0000000..4ecc6af
--- /dev/null
+++ b/plugins/rssfeed/rssfeedplugin.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTRSSFEEDPLUGIN_H
+#define KTRSSFEEDPLUGIN_H
+
+#include <interfaces/plugin.h>
+
+class QString;
+
+
+namespace kt
+{
+ class RssFeedManager;
+
+ /**
+ * @author Alan Jones <skyphyr@gmail.com>
+ * @brief KTorrent RssFeed plugin
+ * Automatically scans rssfeeds for torrent matching regular expressions and loads them.
+ */
+ class RssFeedPlugin : public Plugin
+ {
+ Q_OBJECT
+ public:
+ RssFeedPlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~RssFeedPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual bool versionCheck(const QString& version) const;
+
+ private:
+ RssFeedManager * m_rssFeedManager;
+
+ };
+
+}
+
+#endif
diff --git a/plugins/rssfeed/rssfeedwidget.ui b/plugins/rssfeed/rssfeedwidget.ui
new file mode 100644
index 0000000..55bd27b
--- /dev/null
+++ b/plugins/rssfeed/rssfeedwidget.ui
@@ -0,0 +1,969 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>RssFeedWidget</class>
+<comment>The display widget for the rssfeed tab</comment>
+<author>Alan Jones</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>RssFeedWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>798</width>
+ <height>530</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Rss Feeds</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabs</cstring>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>feeds</cstring>
+ </property>
+ <attribute name="title">
+ <string>Feeds</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSplitter">
+ <property name="name">
+ <cstring>splitter16</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QListBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>feedlist</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>deleteFeed</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>De&amp;lete</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>newFeed</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>feedLayout</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KURLRequester" row="1" column="1">
+ <property name="name">
+ <cstring>feedUrl</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>feedUrlLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;URL</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>feedUrl</cstring>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>feedBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ <property name="title">
+ <string>Articles</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="1" column="1">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>90</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>downloadArticle</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Download</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QTable" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>feedArticles</cstring>
+ </property>
+ <property name="numRows">
+ <number>0</number>
+ </property>
+ <property name="numCols">
+ <number>0</number>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>MultiRow</enum>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>feedRefreshTimeLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Auto&amp;refresh</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>feedAutoRefresh</cstring>
+ </property>
+ </widget>
+ <widget class="QTimeEdit">
+ <property name="name">
+ <cstring>feedAutoRefresh</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="time">
+ <time>
+ <hour>0</hour>
+ <minute>30</minute>
+ <second>0</second>
+ </time>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>feedIgnoreTTL</cstring>
+ </property>
+ <property name="text">
+ <string>I&amp;gnore TTL</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>60</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>refreshFeed</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Refresh</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout47</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>feedTitleLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Title</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>feedTitle</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>feedTitle</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>feedArticleAgeLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Keep Articles (days)</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>feedArticleAge</cstring>
+ </property>
+ <property name="maxValue">
+ <number>3650</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>feedActive</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Active</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>filters</cstring>
+ </property>
+ <attribute name="title">
+ <string>Filters</string>
+ </attribute>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout28</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox12</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>3</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Accept Filters</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QListBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>acceptFilterList</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>deleteAcceptFilter</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>newAcceptFilter</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox13</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Reject Filters</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QListBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>rejectFilterList</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>deleteRejectFilter</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>newRejectFilter</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>filterTitleLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Title</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>filterTitle</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>filterTitle</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>filterActive</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Active</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KEditListBox">
+ <property name="name">
+ <cstring>filterRegExps</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="title">
+ <string>Regular Expressions</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="buttons">
+ <set>Remove|Add</set>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout28</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>filterSeries</cstring>
+ </property>
+ <property name="text">
+ <string>Treat as &amp;Series</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer20</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Preferred</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>processFilter</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Process</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>seriesBox</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32000</width>
+ <height>32000</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Series Criteria</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>filterSansEpisode</cstring>
+ </property>
+ <property name="text">
+ <string>Match Without Episode</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>filterMinSeason</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>filterMaxEpisodeLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Max Episode</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="0" column="3">
+ <property name="name">
+ <cstring>filterMinEpisode</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>filterMaxSeason</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>filterMaxSeasonLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Max Season</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>filterMinSeason</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>filterMinSeasonLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Min Season</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>filterMinSeason</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="3">
+ <property name="name">
+ <cstring>filterMaxEpisode</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>filterMinEpisodeLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Min Episode</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout33</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>testTextLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Test te&amp;xt</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>testText</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>testText</cstring>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>testTestText</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Te&amp;st</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>filterMatchesBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Filter Matches</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTable">
+ <property name="name">
+ <cstring>filterMatches</cstring>
+ </property>
+ <property name="numRows">
+ <number>0</number>
+ </property>
+ <property name="numCols">
+ <number>0</number>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>MultiRow</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>downloadFilterMatch</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Download</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>170</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>deleteFilterMatch</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>filterSeries</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>seriesBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>feedIgnoreTTL</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>feedAutoRefresh</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="2" margin="2"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>keditlistbox.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/rssfeed/rssfilter.cpp b/plugins/rssfeed/rssfilter.cpp
new file mode 100644
index 0000000..6cf1f1f
--- /dev/null
+++ b/plugins/rssfeed/rssfilter.cpp
@@ -0,0 +1,423 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "rssfilter.h"
+
+namespace kt
+{
+
+ FilterMatch::FilterMatch(int season, int episode, QString link, QString time)
+ {
+ m_season = season;
+ m_episode = episode;
+ m_link = link;
+ m_time = time;
+ }
+
+ FilterMatch::FilterMatch(const FilterMatch &other)
+ {
+ *this = other;
+ }
+
+ FilterMatch &FilterMatch::operator=(const FilterMatch &other)
+ {
+ if (&other != this)
+ {
+ m_season = other.season();
+ m_episode = other.episode();
+ m_link = other.link();
+ m_time = other.time();
+ }
+
+ return *this;
+ }
+
+ bool FilterMatch::operator==(const FilterMatch &other) const
+ {
+ return m_link==other.link() && m_season==other.season() && m_episode==other.episode();
+ }
+
+ RssFilter::RssFilter(QObject * parent) : QObject(parent)
+ {
+ m_title = "New";
+ m_active = false;
+ m_series = false;
+ m_sansEpisode = false;
+ m_minSeason = m_minEpisode = m_maxSeason = m_maxEpisode = 0;
+ }
+
+ RssFilter::RssFilter(QString title, bool active, QStringList regExps, bool series, bool sansEpisode,
+ int minSeason, int minEpisode, int maxSeason, int maxEpisode,
+ QValueList<FilterMatch> matches)
+ {
+ m_title = title;
+ m_active = active;
+ m_regExps = regExps;
+ m_series = series;
+ m_sansEpisode = sansEpisode;
+ m_minSeason = minSeason;
+ m_minEpisode = minEpisode;
+ m_maxSeason = maxSeason;
+ m_maxEpisode = maxEpisode;
+ m_matches = matches;
+ }
+
+ RssFilter::RssFilter(const RssFilter &other) : QObject()
+ {
+ *this = other;
+ }
+
+ RssFilter &RssFilter::operator=(const RssFilter &other)
+ {
+ if (&other != this)
+ {
+ m_title = other.title();
+ m_active = other.active();
+ m_regExps = other.regExps();
+ m_series = other.series();
+ m_sansEpisode = other.sansEpisode();
+ m_minSeason = other.minSeason();
+ m_minEpisode = other.minEpisode();
+ m_maxSeason = other.maxSeason();
+ m_maxEpisode = other.maxEpisode();
+ m_matches = other.matches();
+ }
+
+ return *this;
+ }
+
+ void RssFilter::setTitle( const QString& title )
+ {
+ if (m_title != title)
+ {
+ m_title = title;
+ emit titleChanged(title);
+ }
+ }
+
+ void RssFilter::setActive( bool active )
+ {
+ if (m_active != active)
+ {
+ m_active = active;
+
+ emit activeChanged(active);
+ }
+ }
+
+ void RssFilter::setRegExps( const QStringList& regExps )
+ {
+ if (regExps != m_regExps)
+ {
+ m_regExps = regExps;
+
+ emit regExpsChanged(regExps);
+ }
+ }
+
+ void RssFilter::setSeries( bool series )
+ {
+ if (m_series != series)
+ {
+ m_series = series;
+
+ emit seriesChanged(series);
+ }
+ }
+
+ void RssFilter::setSansEpisode( bool sansEpisode )
+ {
+ if (m_sansEpisode != sansEpisode)
+ {
+ m_sansEpisode = sansEpisode;
+
+ emit sansEpisodeChanged(sansEpisode);
+ }
+ }
+
+ void RssFilter::setMinSeason( int minSeason )
+ {
+ if (m_minSeason != minSeason)
+ {
+ m_minSeason = minSeason;
+
+ emit minSeasonChanged(minSeason);
+ }
+ }
+
+ void RssFilter::setMinEpisode( int minEpisode )
+ {
+ if (m_minEpisode != minEpisode)
+ {
+ m_minEpisode = minEpisode;
+
+ emit minEpisodeChanged(minEpisode);
+ }
+ }
+
+ void RssFilter::setMaxSeason( int maxSeason )
+ {
+ if (m_maxSeason != maxSeason)
+ {
+ m_maxSeason = maxSeason;
+
+ emit maxSeasonChanged(maxSeason);
+ }
+ }
+
+ void RssFilter::setMaxEpisode( int maxEpisode )
+ {
+ if (m_maxEpisode != maxEpisode)
+ {
+ m_maxEpisode = maxEpisode;
+
+ emit maxEpisodeChanged(maxEpisode);
+ }
+ }
+
+ void RssFilter::setMatches( const QValueList<FilterMatch>& matches )
+ {
+ if (matches != m_matches)
+ {
+ m_matches = matches;
+
+ emit matchesChanged(matches);
+ }
+ }
+
+ bool RssFilter::episodeInRange(int season, int episode, bool ignoreMatches, bool& alreadyDownloaded)
+ {
+ if (m_minSeason > 0)
+ {
+ if (season < m_minSeason)
+ {
+ return false;
+ }
+ if (season == m_minSeason && m_minEpisode > 0)
+ {
+ if (episode < m_minEpisode)
+ {
+ return false;
+ }
+ }
+ }
+
+ if (m_maxSeason > 0)
+ {
+ if (season > m_maxSeason)
+ {
+ return false;
+ }
+ if (season == m_maxSeason && m_maxEpisode > 0)
+ {
+ if (episode > m_maxEpisode)
+ {
+ return false;
+ }
+ }
+ }
+
+ for (int i=0; i<m_matches.count(); i++)
+ {
+ //if the episode is already in the matches - don't download it
+ if ((*m_matches.at(i)).season() == season &&(*m_matches.at(i)).episode() == episode)
+ {
+ alreadyDownloaded = true;
+ return !ignoreMatches;
+ }
+
+ }
+
+ return true;
+ }
+
+ bool RssFilter::scanArticle( RssArticle article, bool ignoreMatches, bool saveMatch)
+ {
+ if (!m_active && saveMatch)
+ return false;
+
+ QRegExp regEx;
+ regEx.setCaseSensitive(false);
+
+ if (!m_regExps.count())
+ return false;
+
+ for (int i=0; i<m_regExps.count(); i++)
+ {
+ if (m_regExps[i].isEmpty())
+ continue;
+
+ QString curExp = m_regExps[i];
+ bool invert=false;
+
+ if (curExp.startsWith( "!" ))
+ {
+ invert=true;
+ curExp = curExp.remove( 0, 1);
+ }
+
+ regEx.setPattern(curExp);
+
+ if (!invert)
+ {
+ if (!article.title().contains(regEx) && !article.link().prettyURL().contains(regEx) && !article.description().contains(regEx) )
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (article.title().contains(regEx) || article.link().prettyURL().contains(regEx) || article.description().contains(regEx) )
+ {
+ return false;
+ }
+ }
+ }
+
+ int season = 0, episode = 0;
+ bool alreadyDownloaded = false;
+
+ if (m_series)
+ {
+ QStringList episodeFormats;
+ episodeFormats << "s([0-9]{1,2})[de]([0-9]{1,2})[^0-9]" << "[^0-9]([0-9]{1,2})x([0-9]{1,2})[^0-9]" << "[^0-9]([0-9]{1,2})([0-9]{2})[^0-9]";
+ for (int i=0; i<episodeFormats.count(); i++)
+ {
+ regEx.setPattern(*episodeFormats.at(i));
+ if (regEx.search(article.title()) >= 0)
+ {
+ season = (*regEx.capturedTexts().at(1)).toInt();
+ episode = (*regEx.capturedTexts().at(2)).toInt();
+ if (!episodeInRange(season,episode,ignoreMatches,alreadyDownloaded))
+ {
+ return false;
+ }
+ break;
+ }
+
+ if (regEx.search(article.link().prettyURL()) >= 0)
+ {
+ season = (*regEx.capturedTexts().at(1)).toInt();
+ episode = (*regEx.capturedTexts().at(2)).toInt();
+ if (!episodeInRange(season,episode,ignoreMatches,alreadyDownloaded))
+ {
+ return false;
+ }
+ break;
+ }
+
+ if (regEx.search(article.description()) >= 0)
+ {
+ season = (*regEx.capturedTexts().at(1)).toInt();
+ episode = (*regEx.capturedTexts().at(2)).toInt();
+ if (!episodeInRange(season,episode,ignoreMatches,alreadyDownloaded))
+ {
+ return false;
+ }
+ break;
+ }
+ }
+
+ if (!m_sansEpisode)
+ {
+ if (!season && !episode)
+ {
+ //no episode number was found and we're not downloading matches without episode numbers
+ return false;
+ }
+ }
+ }
+
+ if (!alreadyDownloaded && saveMatch)
+ {
+ FilterMatch newMatch(season, episode, article.link().prettyURL());
+ m_matches.append(newMatch);
+ emit matchesChanged(m_matches);
+ }
+
+ return true;
+ }
+
+ void RssFilter::deleteMatch(const QString& link)
+ {
+
+ QValueList<FilterMatch>::iterator it = m_matches.begin();
+ while (it != m_matches.end())
+ {
+ if ((*it).link() == link)
+ {
+ it = m_matches.remove(it);
+ }
+ else
+ {
+ it++;
+ }
+ }
+
+ }
+
+ QDataStream &operator<<( QDataStream &out, const FilterMatch &filterMatch )
+ {
+ out << filterMatch.season() << filterMatch.episode() << filterMatch.time() << filterMatch.link();
+
+ return out;
+ }
+
+ QDataStream &operator>>( QDataStream &in, FilterMatch &filterMatch )
+ {
+ int season, episode;
+ QString time;
+ QString link;
+ in >> season >> episode >> time >> link;
+ filterMatch = FilterMatch(season, episode, link, time);
+
+ return in;
+ }
+
+ QDataStream &operator<<( QDataStream &out, const RssFilter &filter )
+ {
+ out << filter.title() << int(filter.active()) << filter.regExps() << int(filter.series()) << int(filter.sansEpisode()) << filter.minSeason() << filter.minEpisode() << filter.maxSeason() << filter.maxEpisode() << filter.matches();
+
+ return out;
+ }
+
+ QDataStream &operator>>( QDataStream &in, RssFilter &filter )
+ {
+ QString title;
+ int active;
+ QStringList regExps;
+ int series;
+ int sansEpisode;
+ int minSeason;
+ int minEpisode;
+ int maxSeason;
+ int maxEpisode;
+ QValueList<FilterMatch> matches;
+ in >> title >> active >> regExps >> series >> sansEpisode >> minSeason >> minEpisode >> maxSeason >> maxEpisode >> matches;
+
+ filter = RssFilter(title, active, regExps, series, sansEpisode, minSeason, minEpisode, maxSeason, maxEpisode, matches);
+
+ return in;
+ }
+
+ RssFilter::~RssFilter()
+ {
+ }
+
+}
diff --git a/plugins/rssfeed/rssfilter.h b/plugins/rssfeed/rssfilter.h
new file mode 100644
index 0000000..be31d18
--- /dev/null
+++ b/plugins/rssfeed/rssfilter.h
@@ -0,0 +1,151 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef RSSFILTER_H
+#define RSSFILTER_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qdatastream.h>
+#include <qregexp.h>
+
+#include "rssarticle.h"
+
+using namespace RSS;
+
+namespace kt
+{
+ /**
+ * @brief RssFilter Class
+ * @author Alan Jones <skyphyr@gmail.com>
+ *
+ *
+ */
+ class FilterMatch
+ {
+ public:
+
+ FilterMatch() { m_season = 0; m_episode = 0; m_time = QDateTime::currentDateTime().toString(); m_link=QString(); };
+ FilterMatch(int season, int episode, QString link, QString time = QDateTime::currentDateTime().toString());
+ FilterMatch(const FilterMatch &other);
+ FilterMatch &operator=(const FilterMatch &other);
+ bool operator==(const FilterMatch &other) const;
+ ~FilterMatch() {};
+
+ QString link() const { return m_link; }
+ int season() const { return m_season; }
+ int episode() const { return m_episode; }
+ QString time() const { return m_time; }
+
+ void setLink(const QString& link) { m_link = link; }
+ void setSeason(int season) { m_season = season; }
+ void setEpisode(int episode) { m_episode = episode; }
+ void setTime(QString time) { m_time = time; }
+
+ private:
+ int m_season;
+ int m_episode;
+ QString m_link;
+ QString m_time;
+ };
+
+ class RssFilter : public QObject
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Default constructor.
+ */
+ RssFilter(QObject * parent = 0);
+ RssFilter(const RssFilter &other);
+ RssFilter(QString title, bool active, QStringList regexps, bool series, bool sansEpisode,
+ int minSeason, int minEpisode, int maxSeason, int maxEpisode,
+ QValueList<FilterMatch> matches);
+ RssFilter &operator=(const RssFilter &other);
+ ~RssFilter();
+
+ QString title() const { return m_title; }
+ bool active() const { return m_active; }
+ QStringList regExps() const { return m_regExps; }
+ bool series() const { return m_series; }
+ bool sansEpisode() const { return m_sansEpisode; }
+ int minSeason() const { return m_minSeason; }
+ int minEpisode() const { return m_minEpisode; }
+ int maxSeason() const { return m_maxSeason; }
+ int maxEpisode() const { return m_maxEpisode; }
+ QValueList<FilterMatch> matches() const { return m_matches; }
+
+ bool scanArticle(RssArticle article, bool ignoreMatches = true, bool saveMatch = true);
+ void deleteMatch(const QString& link);
+
+ public slots:
+ void setTitle( const QString& title );
+ void setActive( bool active );
+ void setRegExps ( const QStringList& regexps );
+ void setSeries ( bool series );
+ void setSansEpisode ( bool sansEpisode );
+ void setMinSeason( int minSeason );
+ void setMinEpisode( int minEpisode );
+ void setMaxSeason( int maxSeason );
+ void setMaxEpisode( int maxEpisode );
+ void setMatches( const QValueList<FilterMatch>& matches );
+
+ //void scanFilter();
+
+ signals:
+ void titleChanged( const QString& title );
+ void activeChanged( bool active );
+ void regExpsChanged( const QStringList& regexps );
+ void seriesChanged( bool series );
+ void sansEpisodeChanged( bool sansEpisode );
+ void minSeasonChanged (int minSeason);
+ void minEpisodeChanged (int minEpisode);
+ void maxSeasonChanged (int maxSeason);
+ void maxEpisodeChanged (int maxEpisode);
+ void matchesChanged( const QValueList<FilterMatch>& matches );
+
+ void rescanFilter();
+
+ private:
+ QString m_title;
+ bool m_active;
+ QStringList m_regExps;
+ bool m_series;
+ bool m_sansEpisode;
+ int m_minSeason;
+ int m_minEpisode;
+ int m_maxSeason;
+ int m_maxEpisode;
+ QValueList<FilterMatch> m_matches;
+
+ bool episodeInRange(int season, int episode, bool ignoreMatches, bool& alreadyDownloaded);
+
+ };
+
+ QDataStream &operator<<( QDataStream &out, const FilterMatch &filterMatch );
+ QDataStream &operator>>( QDataStream &in, FilterMatch &filterMatch );
+
+ QDataStream &operator<<( QDataStream &out, const RssFilter &filter );
+ QDataStream &operator>>( QDataStream &in, RssFilter &filter );
+
+}
+
+#endif
diff --git a/plugins/rssfeed/rsslinkdownloader.cpp b/plugins/rssfeed/rsslinkdownloader.cpp
new file mode 100644
index 0000000..4b0f390
--- /dev/null
+++ b/plugins/rssfeed/rsslinkdownloader.cpp
@@ -0,0 +1,202 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "rsslinkdownloader.h"
+
+#include <klocale.h>
+#include <kmimetype.h>
+#include <kmessagebox.h>
+
+#include <qfile.h>
+
+#include "../../libktorrent/torrent/bdecoder.h"
+#include "../../libktorrent/torrent/bnode.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ RssLinkDownloader::RssLinkDownloader(CoreInterface* core, QString link, RssFilter * filter, QObject * parent) : QObject (parent)
+ {
+ //tempFile.setAutoDelete(true);
+ m_core = core;
+ firstLink = true;
+ curFilter = filter;
+ if (!KURL(link).isValid())
+ {
+ // no valid URL, so just display an error message
+ KMessageBox::error(0,i18n("Failed to find and download a valid torrent for %1").arg(curLink));
+ QTimer::singleShot(50,this,SLOT(suicide()));
+ }
+ else
+ {
+ //first let's download the link so we can process it to check for the actual torrent
+ curLink = curSubLink = link;
+ curFile = KIO::storedGet(link,false,false);
+ connect(curFile, SIGNAL(result(KIO::Job*)),this,SLOT(processLink( KIO::Job* )));
+ }
+ }
+
+ RssLinkDownloader::~RssLinkDownloader()
+ {
+
+ }
+
+ void RssLinkDownloader::processLink(KIO::Job* jobStatus)
+ {
+
+ if (!jobStatus->error())
+ {
+ //the file downloaded ok - so let's check if it's a torrent
+ KMimeType linkType = *KMimeType::findByContent(curFile->data());
+ if (linkType.is("text/html"))
+ {
+ if (firstLink)
+ {
+ KURL url = curLink;
+ //let's go through the data and populate our sublink array
+ QTextStream html(curFile->data(), IO_ReadOnly);
+
+ //go through a line at a time checking for a torrent
+ QString htmlline = html.readLine();
+ while (!htmlline.isNull())
+ {
+ QRegExp hrefTags = QString("<A.*HREF.*</A");
+ hrefTags.setCaseSensitive(false);
+ hrefTags.setMinimal(true);
+
+ int matchPos = 0;
+ while (htmlline.find(hrefTags, matchPos) >= 0)
+ {
+ matchPos += hrefTags.matchedLength();
+ //we're found an <a href tag - let's check it if contains download
+ QRegExp hrefText = QString("d(own)?load");
+ hrefText.setCaseSensitive(false);
+
+ if (!hrefTags.capturedTexts()[0].contains(hrefText))
+ {
+ //link text doesn't contain dload/download
+ continue;
+ }
+
+ //we're found an <a href tag - now let's the the url out of it
+ hrefText = QString("HREF=\"?([^\">< ]*)[\" ]");
+ hrefText.setCaseSensitive(false);
+
+ hrefTags.capturedTexts()[0].find(hrefText);
+ //lets get the captured
+ QString hrefLink = hrefText.capturedTexts()[1];
+
+ if (hrefLink.startsWith("/"))
+ {
+ hrefLink = url.protocol() + "://" + url.host() + hrefLink;
+ }
+ else if (!hrefLink.startsWith("http://", false))
+ {
+ hrefLink = url.url().left(url.url().findRev("/")+1) + hrefLink;
+ }
+
+ subLinks.append(hrefLink);
+
+ }
+
+ //run the query again
+ htmlline = html.readLine();
+ }
+
+
+ firstLink = false;
+ }
+ }
+ else
+ {
+
+ //I know this may check a file which we've already been told is html, but sometimes it lies
+ try
+ {
+ //last ditched brute force attempt to check if it's a torrent file
+ BNode* node = 0;
+ BDecoder decoder(curFile->data(),false);
+ node = decoder.decode();
+ BDictNode* dict = dynamic_cast<BDictNode*>(node);
+
+ if (dict)
+ {
+ delete node;
+ node = dict = 0;
+
+ if (curFilter)
+ {
+ m_core->loadSilently( curSubLink );
+ emit linkDownloaded( curLink, 3);
+ }
+ else
+ {
+ m_core->load( curSubLink );
+ emit linkDownloaded( curLink, 1);
+ }
+
+ //delete ourself and finish
+ deleteLater();
+ return;
+ }
+
+
+ }
+ catch (...)
+ {
+ //we can just ignore any errors here
+ }
+ }
+
+ }
+ //curFile->deleteLater();
+
+ //check for the next item
+ if (subLinks.isEmpty())
+ {
+ if (curFilter)
+ {
+ //we've failed to download a torrent for this match
+ curFilter->deleteMatch( curLink );
+ }
+ else
+ {
+ //failed to download a selected article from a feed
+ KMessageBox::error(0,i18n("Failed to find and download a valid torrent for %1").arg(curLink));
+ }
+ deleteLater();
+ }
+ else
+ {
+ curSubLink = subLinks.first();
+ subLinks.pop_front();
+ curFile = KIO::storedGet(curSubLink,false,false);
+ connect(curFile, SIGNAL(result(KIO::Job*)),this,SLOT(processLink( KIO::Job* )));
+ }
+ }
+
+
+ void RssLinkDownloader::suicide()
+ {
+ deleteLater();
+ }
+
+} \ No newline at end of file
diff --git a/plugins/rssfeed/rsslinkdownloader.h b/plugins/rssfeed/rsslinkdownloader.h
new file mode 100644
index 0000000..2266f29
--- /dev/null
+++ b/plugins/rssfeed/rsslinkdownloader.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Alan Jones *
+ * skyphyr@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef RSSLINKDOWNLOADER_H
+#define RSSLINKDOWNLOADER_H
+
+#include <kio/job.h>
+#include <ktempfile.h>
+
+#include <interfaces/coreinterface.h>
+#include <torrent/globals.h>
+#include <util/log.h>
+#include <util/constants.h>
+
+#include <qstring.h>
+
+#include "rssfilter.h"
+#include "rssarticle.h"
+
+using namespace RSS;
+
+namespace kt
+{
+ /**
+ * @brief RssLinkDownloader Class
+ * @author Alan Jones <skyphyr@gmail.com>
+ *
+ *
+ */
+
+ class RssLinkDownloader : public QObject
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Default constructor.
+ */
+ RssLinkDownloader(CoreInterface* core, QString link, RssFilter * filter = 0, QObject * parent = 0);
+
+ ~RssLinkDownloader();
+
+
+ public slots:
+ void processLink(KIO::Job* jobStatus);
+ void suicide();
+
+ signals:
+ void linkDownloaded( QString link, int downloaded );
+
+ private:
+ KIO::StoredTransferJob * curFile;
+ QString curLink, curSubLink;
+ QStringList subLinks;
+ RssFilter * curFilter;
+ bool firstLink;
+
+ //KTempFile tempFile;
+
+ CoreInterface* m_core;
+ };
+
+
+}
+
+#endif
diff --git a/plugins/scanfolder/Makefile.am b/plugins/scanfolder/Makefile.am
new file mode 100644
index 0000000..eb8cc52
--- /dev/null
+++ b/plugins/scanfolder/Makefile.am
@@ -0,0 +1,31 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+kde_module_LTLIBRARIES = ktscanfolderplugin.la
+
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktscanfolderplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+# rc file containing the GUI for the plugin
+# pluginsdir = $(kde_datadir)/ktscanfolderplugin
+# plugins_DATA = ktscanfolderpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+
+rcdir = $(kde_datadir)/ktorrent
+
+kde_kcfg_DATA = ktscanfolderplugin.kcfg
+kde_services_DATA = ktscanfolderplugin.desktop
+
+noinst_HEADERS = scanfolderplugin.h scanfolderprefpage.h \
+ scanfolderprefpagewidget.h scanfolder.h
+ktscanfolderplugin_la_SOURCES = scanfolderplugin.cpp \
+ scanfolderpluginsettings.kcfgc scanfolderprefpage.cpp sfprefwidgetbase.ui scanfolderprefpagewidget.cpp \
+ scanfolder.cpp
+ktscanfolderplugin_la_LIBADD = $(LIB_KPARTS) ../../libktorrent/libktorrent.la \
+ $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KFILE)
+
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/scanfolder/ktscanfolderplugin.desktop b/plugins/scanfolder/ktscanfolderplugin.desktop
new file mode 100644
index 0000000..b6180de
--- /dev/null
+++ b/plugins/scanfolder/ktscanfolderplugin.desktop
@@ -0,0 +1,26 @@
+[Desktop Entry]
+Name=ScanFolderPlugin
+Name[bg]=Приставка за сканиране
+Name[cs]=Modul prohledávání složek
+Name[da]=Plugin for mappesøgning
+Name[de]=Ordner-Durchsuchen-Modul
+Name[el]=Πρόσθετο σάρωσης φακέλων
+Name[et]=Kataloogi uurimise plugin
+Name[it]=Plugin scansione cartella
+Name[nb]=Katalogundersøkingsmdoul
+Name[nds]=Moduul för't Dörkieken vun Ornern
+Name[nl]=Mapscanplugin
+Name[pl]=Wtyczka przeszukiwania katalogów
+Name[pt_BR]=Plugins de Busca
+Name[sk]=ScanFolder Plugin
+Name[sr]=Прикључак прегледа фасцикле
+Name[sr@Latn]=Priključak pregleda fascikle
+Name[sv]=Insticksprogram för katalogsökning
+Name[tr]=Dizin Tarama Eklentisi
+Name[uk]=Втулок сканування тек
+Name[xx]=xxScanFolderPluginxx
+Name[zh_CN]=扫描文件夹插件
+Name[zh_TW]=掃描資料夾外掛程式
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktscanfolderplugin
diff --git a/plugins/scanfolder/ktscanfolderplugin.kcfg b/plugins/scanfolder/ktscanfolderplugin.kcfg
new file mode 100644
index 0000000..f9b919f
--- /dev/null
+++ b/plugins/scanfolder/ktscanfolderplugin.kcfg
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <group name="general">
+ <entry name="useFolder1" type="Bool">
+ <label>Enable scanning of folder1?</label>
+ <default>FALSE</default>
+ </entry>
+
+ <entry name="useFolder2" type="Bool">
+ <label>Enable scanning of folder2?</label>
+ <default>FALSE</default>
+ </entry>
+
+ <entry name="useFolder3" type="Bool">
+ <label>Enable scanning of folder3?</label>
+ <default>FALSE</default>
+ </entry>
+
+ <entry name="folder1" type="String">
+ <label>Folder1 path</label>
+ <default></default>
+ </entry>
+
+ <entry name="folder2" type="String">
+ <label>Folder2 path</label>
+ <default></default>
+ </entry>
+
+ <entry name="folder3" type="String">
+ <label>Folder3 path</label>
+ <default></default>
+ </entry>
+
+ <entry name="openSilently" type="Bool">
+ <label>Whether to open torrent silently or not.</label>
+ <default>FALSE</default>
+ </entry>
+
+ <entry name="actionDelete" type="Bool">
+ <label>Delete action checked.</label>
+ <default>FALSE</default>
+ </entry>
+
+ <entry name="actionMove" type="Bool">
+ <label>Move action checked.</label>
+ <default>FALSE</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/scanfolder/scanfolder.cpp b/plugins/scanfolder/scanfolder.cpp
new file mode 100644
index 0000000..3c30299
--- /dev/null
+++ b/plugins/scanfolder/scanfolder.cpp
@@ -0,0 +1,273 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "scanfolder.h"
+
+#include <kdirlister.h>
+#include <kfileitem.h>
+#include <klocale.h>
+#include <kio/job.h>
+
+#include <qstring.h>
+#include <qobject.h>
+#include <qfile.h>
+#include <qvaluelist.h>
+
+#include <torrent/globals.h>
+#include <util/log.h>
+#include <util/fileops.h>
+#include <util/functions.h>
+#include <util/constants.h>
+
+#include <torrent/bnode.h>
+#include <torrent/bdecoder.h>
+
+#include <interfaces/coreinterface.h>
+
+using namespace bt;
+
+namespace kt
+{
+
+ ScanFolder::ScanFolder(CoreInterface* core, QString& dir, LoadedTorrentAction action, bool openSilently)
+ : m_core(core), m_dir(0), m_loadedAction(action), m_openSilently(openSilently)
+ {
+ m_dir = new KDirLister();
+
+ if(!m_dir->openURL(dir)) {
+ m_valid = false;
+ return;
+ } else
+ m_valid = true;
+
+ m_dir->setShowingDotFiles(true);
+
+ connect(m_dir, SIGNAL(newItems( const KFileItemList& )), this, SLOT(onNewItems( const KFileItemList& )));
+ connect(m_core, SIGNAL(loadingFinished( const KURL&, bool, bool )), this, SLOT(onLoadingFinished( const KURL&, bool, bool )));
+ connect(&m_incomplePollingTimer,SIGNAL(timeout()),this,SLOT(onIncompletePollingTimeout()));
+ }
+
+
+ ScanFolder::~ScanFolder()
+ {
+// Out() << "UNLOADING SCANFOLDER: " << m_dir->url().path() << endl;
+ delete m_dir;
+ }
+
+ void ScanFolder::onNewItems(const KFileItemList& items)
+ {
+ KFileItemList list = items;
+ KFileItem* file;
+ for(file=list.first(); file; file=list.next())
+ {
+ QString name = file->name();
+ QString dirname = m_dir->url().path();
+ QString filename = dirname + bt::DirSeparator() + name;
+
+ if(!name.endsWith(".torrent"))
+ continue;
+
+ if(name.startsWith("."))
+ {
+ //Check if corresponding torrent exists
+ if(!QFile::exists(m_dir->url().path() + bt::DirSeparator() + name.right(name.length() - 1)) && (m_loadedAction == defaultAction))
+ QFile::remove(filename);
+
+ continue;
+ }
+
+ KURL source;
+ source.setPath(filename);
+
+ //If torrent has it's hidden complement - skip it.
+ if(QFile::exists(dirname + "/." + name))
+ continue;
+
+ if (incomplete(source))
+ {
+ // incomplete file, try this again in 10 seconds
+ bt::Out(SYS_SNF|LOG_NOTICE) << "ScanFolder : incomplete file " << source << endl;
+ m_incompleteURLs.append(source);
+ if (m_incompleteURLs.count() == 1)
+ {
+ // first URL so start the poll timer
+ // lets poll every 10 seconds
+ m_incomplePollingTimer.start(10000,false);
+ }
+ }
+ else
+ {
+ bt::Out(SYS_SNF|LOG_NOTICE) << "ScanFolder : found " << source << endl;
+ //Add pending entry...
+ m_pendingURLs.push_back(source);
+
+ //Load torrent
+ if(m_openSilently)
+ m_core->loadSilently(source);
+ else
+ m_core->load(source);
+ }
+ }
+ }
+
+ void ScanFolder::onLoadingFinished(const KURL & url, bool success, bool canceled)
+ {
+ if(m_pendingURLs.empty() || !success)
+ return;
+
+ //search for entry
+ QValueList<KURL>::iterator it = m_pendingURLs.find(url);
+
+ //if no entry is found than this torrent was not started by this plugin so - quit
+ if(it == m_pendingURLs.end())
+ return;
+
+ //remove this entry
+ m_pendingURLs.erase(it);
+
+ if(canceled)
+ return;
+
+ QString name = url.filename(false);
+ QString dirname = m_dir->url().path();
+ QString filename = dirname + "/" + name;
+ KURL destination(dirname + "/" + i18n("loaded") + "/" + name);
+
+ switch(m_loadedAction) {
+ case deleteAction:
+ //If torrent has it's hidden complement - remove it too.
+ if(QFile::exists(dirname + "/." + name))
+ QFile::remove(dirname + "/." + name);
+ // Out() << "Deleting: " << name.ascii() << endl;
+ QFile::remove(filename);
+ break;
+ case moveAction:
+ // Out() << "Moving: " << name.ascii() << endl;
+ //If torrent has it's hidden complement - remove it too.
+ if(QFile::exists(dirname + "/." + name))
+ QFile::remove(dirname + "/." + name);
+
+ // NetAccess considered harmfull !!!
+ KIO::file_move(url, destination);
+ break;
+ case defaultAction:
+ QFile f(dirname + "/." + name);
+ f.open(IO_WriteOnly);
+ f.close();
+ break;
+ }
+ }
+
+ void ScanFolder::setOpenSilently(bool theValue)
+ {
+ m_openSilently = theValue;
+ }
+
+ void ScanFolder::setLoadedAction(const LoadedTorrentAction& theValue)
+ {
+ m_loadedAction = theValue;
+
+ QDir tmp(m_dir->url().path());
+
+ if( (m_loadedAction == moveAction) && !tmp.exists(i18n("loaded"), false))
+ tmp.mkdir(i18n("loaded"), false);
+ }
+
+ void ScanFolder::setFolderUrl(QString& url)
+ {
+ if(!m_dir->openURL(url)) {
+ m_valid = false;
+ return;
+ } else
+ m_valid = true;
+ }
+
+ bool ScanFolder::incomplete(const KURL & src)
+ {
+ // try to decode file, if it is syntactically correct, we can try to load it
+ QFile fptr(src.path());
+ if (!fptr.open(IO_ReadOnly))
+ return false;
+
+ try
+ {
+ QByteArray data(fptr.size());
+ fptr.readBlock(data.data(),fptr.size());
+ bt::BDecoder dec(data,false);
+ bt::BNode* n = dec.decode();
+ if (n)
+ {
+ // valid node, so file is complete
+ delete n;
+ return false;
+ }
+ else
+ {
+ // decoding failed so incomplete
+ return true;
+ }
+ }
+ catch (...)
+ {
+ // any error means shit happened and the file is incomplete
+ return true;
+ }
+ return false;
+ }
+
+ void ScanFolder::onIncompletePollingTimeout()
+ {
+ bt::Out(SYS_SNF|LOG_NOTICE) << "ScanFolder : checking incomplete files" << endl;
+ for (QValueList<KURL>::iterator i = m_incompleteURLs.begin(); i != m_incompleteURLs.end();)
+ {
+ KURL source = *i;
+ if (!bt::Exists(source.path()))
+ {
+ // doesn't exist anymore, so throw out of list
+ i = m_incompleteURLs.erase(i);
+ }
+ else if (!incomplete(source))
+ {
+ bt::Out(SYS_SNF|LOG_NOTICE) << "ScanFolder : incomplete file " << source << " appears to be completed " << endl;
+ //Add pending entry...
+ m_pendingURLs.push_back(source);
+
+ //Load torrent
+ if(m_openSilently)
+ m_core->loadSilently(source);
+ else
+ m_core->load(source);
+
+ // remove from incomplete list
+ i = m_incompleteURLs.erase(i);
+ }
+ else
+ {
+ bt::Out(SYS_SNF|LOG_NOTICE) << "ScanFolder : still incomplete : " << source << endl;
+ i++;
+ }
+ }
+
+ // stop timer when no incomple URL's are left
+ if (m_incompleteURLs.count() == 0)
+ m_incomplePollingTimer.stop();
+ }
+}
+
+#include "scanfolder.moc"
diff --git a/plugins/scanfolder/scanfolder.h b/plugins/scanfolder/scanfolder.h
new file mode 100644
index 0000000..8e8c27c
--- /dev/null
+++ b/plugins/scanfolder/scanfolder.h
@@ -0,0 +1,111 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef SCANFOLDER_H
+#define SCANFOLDER_H
+
+#include <kdirlister.h>
+#include <kfileitem.h>
+#include <qstring.h>
+#include <qobject.h>
+#include <qdir.h>
+#include <qvaluelist.h>
+#include <qtimer.h>
+#include <kurl.h>
+
+namespace kt
+{
+ ///Action to perform after loading torrent.
+ enum LoadedTorrentAction
+ {
+ deleteAction,
+ moveAction,
+ defaultAction
+ };
+
+ class CoreInterface;
+
+ /**
+ * @brief Scanned folder class.
+ * @author Ivan Vasić <ivasic@gmail.com>
+ *
+ * It will monitor m_dir directory for changes and automatically pass new torrents to core for loading.
+ * After loading, it will perform specified action which can be:
+ * 1. Deleting torrent in question
+ * 2. Moving torrent to 'loaded' subdirectory
+ * 3. Default action (neither 1. nor 2.)
+ * @see LoadedTorrentAction
+ *
+ */
+ class ScanFolder : public QObject
+ {
+ Q_OBJECT
+ public:
+
+ /**
+ * Default constructor.
+ * @param core Pointer to core interface
+ * @param dir Full directory path
+ * @param action Action to perform on loaded torrents.
+ * @param openSilently Wheather to open torrent silently or not.
+ */
+ ScanFolder(CoreInterface* core, QString& dir, LoadedTorrentAction action = defaultAction, bool openSilently = true);
+ ~ScanFolder();
+
+ ///Accessor method for m_openSilently.
+ bool openSilently() const { return m_openSilently; }
+ ///Accessor method for m_openSilently
+ void setOpenSilently(bool theValue);
+
+ ///Accessor method for m_loadedAction.
+ void setLoadedAction(const LoadedTorrentAction& theValue);
+ ///Accessor method for m_loadedAction.
+ LoadedTorrentAction loadedAction() const { return m_loadedAction; }
+
+ ///Returns true if this object is valid, that is - weather directory is valid and this object does its work.
+ bool isValid() const { return m_valid; }
+
+ ///Sets directory path
+ void setFolderUrl(QString& url);
+
+ public slots:
+ void onNewItems(const KFileItemList &items);
+ void onLoadingFinished(const KURL & url,bool success,bool canceled);
+ void onIncompletePollingTimeout();
+
+ private:
+ /// Check if the URL is a complete file
+ bool incomplete(const KURL & src);
+
+ private:
+ CoreInterface* m_core;
+
+ bool m_valid;
+ KDirLister* m_dir;
+
+ LoadedTorrentAction m_loadedAction;
+ bool m_openSilently;
+
+ QValueList<KURL> m_pendingURLs;
+ QValueList<KURL> m_incompleteURLs;
+
+ QTimer m_incomplePollingTimer;
+ };
+}
+#endif
diff --git a/plugins/scanfolder/scanfolderplugin.cpp b/plugins/scanfolder/scanfolderplugin.cpp
new file mode 100644
index 0000000..a994243
--- /dev/null
+++ b/plugins/scanfolder/scanfolderplugin.cpp
@@ -0,0 +1,187 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+
+#include <interfaces/coreinterface.h>
+#include <interfaces/guiinterface.h>
+#include <interfaces/plugin.h>
+#include <util/constants.h>
+#include <util/log.h>
+
+#include <qstring.h>
+#include <qfile.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kglobal.h>
+
+#include "scanfolder.h"
+#include "scanfolderplugin.h"
+#include "scanfolderprefpage.h"
+#include "scanfolderpluginsettings.h"
+
+using namespace bt;
+
+K_EXPORT_COMPONENT_FACTORY(ktscanfolderplugin,KGenericFactory<kt::ScanFolderPlugin>("scanfolderplugin"))
+
+namespace kt
+{
+ const QString NAME = "Scan Folder";
+ const QString AUTHOR = "Ivan Vasic";
+ const QString EMAIL = "ivasic@gmail.com";
+ const QString DESCRIPTION = i18n("Automatically scans directories for torrent files and loads them.");
+
+ ScanFolderPlugin::ScanFolderPlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name, args,NAME,i18n("Scan Folder"),AUTHOR,EMAIL,DESCRIPTION,"view_sidetree")
+ {
+// setXMLFile("ktscanfolderpluginui.rc");
+ m_sf1 = 0;
+ m_sf2 = 0;
+ m_sf3 = 0;
+ }
+
+
+ ScanFolderPlugin::~ScanFolderPlugin()
+ {
+ }
+
+ void ScanFolderPlugin::load()
+ {
+ pref = new ScanFolderPrefPage(this);
+ getGUI()->addPrefPage(pref);
+ updateScanFolders();
+ }
+
+ void ScanFolderPlugin::unload()
+ {
+ getGUI()->removePrefPage(pref);
+ delete pref;
+ pref = 0;
+
+ if(m_sf1)
+ delete m_sf1;
+ m_sf1 = 0;
+
+ if(m_sf2)
+ delete m_sf2;
+ m_sf2 = 0;
+
+ if(m_sf3)
+ delete m_sf3;
+ m_sf3 = 0;
+ }
+
+ void ScanFolderPlugin::updateScanFolders()
+ {
+ QString sfPath1 = ScanFolderPluginSettings::folder1();
+ QString sfPath2 = ScanFolderPluginSettings::folder2();
+ QString sfPath3 = ScanFolderPluginSettings::folder3();
+
+ bool valid1 = QFile::exists(sfPath1);
+ bool valid2 = QFile::exists(sfPath2);
+ bool valid3 = QFile::exists(sfPath3);
+
+ bool usesf1 = ScanFolderPluginSettings::useFolder1() && valid1;
+ bool usesf2 = ScanFolderPluginSettings::useFolder2() && valid2;
+ bool usesf3 = ScanFolderPluginSettings::useFolder3() && valid3;
+
+ bool silently = ScanFolderPluginSettings::openSilently();
+
+ LoadedTorrentAction action;
+
+ if(ScanFolderPluginSettings::actionDelete())
+ action = deleteAction;
+ else if(ScanFolderPluginSettings::actionMove())
+ action = moveAction;
+ else
+ action = defaultAction;
+
+
+ if(usesf1)
+ {
+ if(!m_sf1)
+ m_sf1 = new ScanFolder(getCore(), sfPath1, action, silently);
+ else
+ {
+ m_sf1->setFolderUrl(sfPath1);
+ m_sf1->setLoadedAction(action);
+ m_sf1->setOpenSilently(silently);
+ }
+ }
+ else
+ {
+ if(m_sf1)
+ delete m_sf1;
+ m_sf1 = 0;
+ }
+
+ if(usesf2)
+ {
+ if(!m_sf2)
+ m_sf2 = new ScanFolder(getCore(), sfPath1, action, silently);
+ else
+ {
+ m_sf2->setFolderUrl(sfPath1);
+ m_sf2->setLoadedAction(action);
+ m_sf2->setOpenSilently(silently);
+ }
+ }
+ else
+ {
+ if(m_sf2)
+ delete m_sf2;
+ m_sf2 = 0;
+ }
+
+ if(usesf3)
+ {
+ if(!m_sf3)
+ m_sf3 = new ScanFolder(getCore(), sfPath1, action, silently);
+ else
+ {
+ m_sf3->setFolderUrl(sfPath1);
+ m_sf3->setLoadedAction(action);
+ m_sf3->setOpenSilently(silently);
+ }
+ }
+ else
+ {
+ if(m_sf3)
+ delete m_sf3;
+ m_sf3 = 0;
+ }
+
+ //update config file
+ if(!valid1)
+ ScanFolderPluginSettings::setUseFolder1(false);
+ if(!valid2)
+ ScanFolderPluginSettings::setUseFolder2(false);
+ if(!valid3)
+ ScanFolderPluginSettings::setUseFolder3(false);
+
+ ScanFolderPluginSettings::writeConfig();
+
+ }
+
+ bool ScanFolderPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+}
diff --git a/plugins/scanfolder/scanfolderplugin.h b/plugins/scanfolder/scanfolderplugin.h
new file mode 100644
index 0000000..2ef50c1
--- /dev/null
+++ b/plugins/scanfolder/scanfolderplugin.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTSCANFOLDERPLUGIN_H
+#define KTSCANFOLDERPLUGIN_H
+
+#include <interfaces/plugin.h>
+
+class QString;
+
+
+namespace kt
+{
+ class ScanFolder;
+ class ScanFolderPrefPage;
+
+ /**
+ * @author Ivan Vasic <ivasic@gmail.com>
+ * @brief KTorrent ScanFolder plugin
+ * Automatically scans selected folder for torrent files and loads them.
+ */
+ class ScanFolderPlugin : public Plugin
+ {
+ Q_OBJECT
+ public:
+ ScanFolderPlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~ScanFolderPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual bool versionCheck(const QString& version) const;
+
+ void updateScanFolders();
+
+ private:
+ ScanFolder* m_sf1;
+ ScanFolder* m_sf2;
+ ScanFolder* m_sf3;
+
+ ScanFolderPrefPage* pref;
+ };
+
+}
+
+#endif
diff --git a/plugins/scanfolder/scanfolderpluginsettings.kcfgc b/plugins/scanfolder/scanfolderpluginsettings.kcfgc
new file mode 100644
index 0000000..af1ebbc
--- /dev/null
+++ b/plugins/scanfolder/scanfolderpluginsettings.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=ktscanfolderplugin.kcfg
+ClassName=ScanFolderPluginSettings
+Namespace=kt
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables \ No newline at end of file
diff --git a/plugins/scanfolder/scanfolderprefpage.cpp b/plugins/scanfolder/scanfolderprefpage.cpp
new file mode 100644
index 0000000..9abbf0d
--- /dev/null
+++ b/plugins/scanfolder/scanfolderprefpage.cpp
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "scanfolderplugin.h"
+#include "scanfolderprefpage.h"
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kiconloader.h>
+
+
+
+namespace kt
+{
+
+ ScanFolderPrefPage::ScanFolderPrefPage(ScanFolderPlugin* plugin)
+ : PrefPageInterface(i18n("ScanFolder"), i18n("ScanFolder Options"),
+ KGlobal::iconLoader()->loadIcon("view_sidetree",KIcon::NoGroup)), m_plugin(plugin)
+ {}
+
+
+ ScanFolderPrefPage::~ScanFolderPrefPage()
+ {}
+
+ bool ScanFolderPrefPage::apply()
+ {
+ if(m_widget)
+ m_widget->apply();
+
+ m_plugin->updateScanFolders();
+
+ return true;
+ }
+
+ void ScanFolderPrefPage::createWidget(QWidget* parent)
+ {
+ m_widget = new ScanFolderPrefPageWidget(parent);
+ }
+
+ void ScanFolderPrefPage::updateData()
+ {
+ }
+
+ void ScanFolderPrefPage::deleteWidget()
+ {
+ delete m_widget;
+ }
+
+}
diff --git a/plugins/scanfolder/scanfolderprefpage.h b/plugins/scanfolder/scanfolderprefpage.h
new file mode 100644
index 0000000..539a5fe
--- /dev/null
+++ b/plugins/scanfolder/scanfolderprefpage.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTSCANFOLDERPREFPAGE_H
+#define KTSCANFOLDERPREFPAGE_H
+
+#include <interfaces/prefpageinterface.h>
+
+#include "scanfolderplugin.h"
+#include "scanfolderprefpagewidget.h"
+
+namespace kt
+{
+
+ /**
+ * ScanFolder plugin preferences page
+ * @author Ivan Vasić <ivasic@gmail.com>
+ */
+ class ScanFolderPrefPage : public PrefPageInterface
+ {
+ public:
+ ScanFolderPrefPage(ScanFolderPlugin* plugin);
+ virtual ~ScanFolderPrefPage();
+
+ virtual bool apply();
+ virtual void createWidget(QWidget* parent);
+ virtual void updateData();
+ virtual void deleteWidget();
+
+ private:
+ ScanFolderPlugin* m_plugin;
+ ScanFolderPrefPageWidget* m_widget;
+ };
+
+}
+
+#endif
diff --git a/plugins/scanfolder/scanfolderprefpagewidget.cpp b/plugins/scanfolder/scanfolderprefpagewidget.cpp
new file mode 100644
index 0000000..b577a88
--- /dev/null
+++ b/plugins/scanfolder/scanfolderprefpagewidget.cpp
@@ -0,0 +1,107 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "scanfolderprefpagewidget.h"
+#include "scanfolderpluginsettings.h"
+
+#include <qwidget.h>
+#include <qstring.h>
+#include <qcheckbox.h>
+#include <qfile.h>
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kurlrequester.h>
+
+namespace kt
+{
+ ScanFolderPrefPageWidget::ScanFolderPrefPageWidget(QWidget *parent, const char *name)
+ :SfPrefPageWidgetBase(parent, name)
+ {
+ use1->setChecked(ScanFolderPluginSettings::useFolder1());
+ use2->setChecked(ScanFolderPluginSettings::useFolder2());
+ use3->setChecked(ScanFolderPluginSettings::useFolder3());
+
+ url1->setURL(ScanFolderPluginSettings::folder1());
+ url2->setURL(ScanFolderPluginSettings::folder2());
+ url3->setURL(ScanFolderPluginSettings::folder3());
+
+ openSilently->setChecked(ScanFolderPluginSettings::openSilently());
+ deleteCheck->setChecked(ScanFolderPluginSettings::actionDelete());
+ moveCheck->setChecked(ScanFolderPluginSettings::actionMove());
+
+ url1->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly);
+ url2->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly);
+ url3->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly);
+ }
+
+ void ScanFolderPrefPageWidget::apply()
+ {
+ bool usesf1 = use1->isChecked();
+ bool usesf2 = use2->isChecked();
+ bool usesf3 = use3->isChecked();
+
+ QString sfPath1 = url1->url();
+ QString sfPath2 = url2->url();
+ QString sfPath3 = url3->url();
+
+ bool silently = openSilently->isChecked();
+ bool deleteChecked = deleteCheck->isChecked();
+ bool moveChecked = moveCheck->isChecked();
+
+ ScanFolderPluginSettings::setOpenSilently(silently);
+ ScanFolderPluginSettings::setActionDelete(deleteChecked);
+ ScanFolderPluginSettings::setActionMove(moveChecked);
+
+ QString message = i18n( "ScanFolder - Folder %1: Invalid URL or folder does not exist. Please, choose a valid directory." );
+ if(!QFile::exists(sfPath1) && usesf1)
+ {
+ KMessageBox::sorry(0, message.arg( 1 ) );
+ usesf1 = false;
+ }
+ else
+ ScanFolderPluginSettings::setFolder1(sfPath1);
+
+ if(!QFile::exists(sfPath2) && usesf2)
+ {
+ KMessageBox::sorry(0, message.arg( 2 ) );
+ usesf2 = false;
+ }
+ else
+ ScanFolderPluginSettings::setFolder2(sfPath2);
+
+ if(!QFile::exists(sfPath3) && usesf3)
+ {
+ KMessageBox::sorry(0, message.arg( 3 ) );
+ usesf3 = false;
+ }
+ else
+ ScanFolderPluginSettings::setFolder3(sfPath3);
+
+
+ ScanFolderPluginSettings::setUseFolder1(usesf1);
+ ScanFolderPluginSettings::setUseFolder2(usesf2);
+ ScanFolderPluginSettings::setUseFolder3(usesf3);
+
+ ScanFolderPluginSettings::writeConfig();
+ }
+
+}
+#include "scanfolderprefpagewidget.moc"
diff --git a/plugins/scanfolder/scanfolderprefpagewidget.h b/plugins/scanfolder/scanfolderprefpagewidget.h
new file mode 100644
index 0000000..480b3b0
--- /dev/null
+++ b/plugins/scanfolder/scanfolderprefpagewidget.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef SCANFOLDERPREFPAGEWIDGET_H
+#define SCANFOLDERPREFPAGEWIDGET_H
+
+#include "sfprefwidgetbase.h"
+
+namespace kt
+{
+ class ScanFolderPrefPageWidget: public SfPrefPageWidgetBase
+ {
+ Q_OBJECT
+ public:
+ ScanFolderPrefPageWidget(QWidget *parent = 0, const char *name = 0);
+
+ void apply();
+ };
+}
+#endif
diff --git a/plugins/scanfolder/sfprefwidgetbase.ui b/plugins/scanfolder/sfprefwidgetbase.ui
new file mode 100644
index 0000000..8b2c83a
--- /dev/null
+++ b/plugins/scanfolder/sfprefwidgetbase.ui
@@ -0,0 +1,272 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>SfPrefPageWidgetBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SfPrefPageWidgetBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>546</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>ScanFolder Preferences</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>use1</cstring>
+ </property>
+ <property name="text">
+ <string>Scan folder &amp;1</string>
+ </property>
+ <property name="accel">
+ <string>Alt+1</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>use2</cstring>
+ </property>
+ <property name="text">
+ <string>Scan folder &amp;2</string>
+ </property>
+ <property name="accel">
+ <string>Alt+2</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>use3</cstring>
+ </property>
+ <property name="text">
+ <string>Scan folder &amp;3</string>
+ </property>
+ <property name="accel">
+ <string>Alt+3</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup" row="3" column="0">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>moveCheck</cstring>
+ </property>
+ <property name="text">
+ <string>Move to "loaded" directory &amp;after loading</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Move torrent to "loaded" directory after loading it</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>openSilently</cstring>
+ </property>
+ <property name="text">
+ <string>Open torrents silentl&amp;y</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Do not show 'Select files to download' dialog</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>deleteCheck</cstring>
+ </property>
+ <property name="text">
+ <string>Delete after loadin&amp;g</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Deletes torrent after loading</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0">
+ <property name="name">
+ <cstring>groupBox9</cstring>
+ </property>
+ <property name="title">
+ <string>Folders to scan</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>url1</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1">
+ <property name="name">
+ <cstring>url2</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="2" column="1">
+ <property name="name">
+ <cstring>url3</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Folder 1:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kURLRequester1</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Folder 2:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kURLRequester4</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Folder 3:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kURLRequester5</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="4" column="0">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>90</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>use1</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>url1</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>use2</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>url2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>use3</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>url3</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>deleteCheck</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>moveCheck</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>moveCheck</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>deleteCheck</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>use1</tabstop>
+ <tabstop>use2</tabstop>
+ <tabstop>use3</tabstop>
+ <tabstop>url1</tabstop>
+ <tabstop>url2</tabstop>
+ <tabstop>url3</tabstop>
+ <tabstop>openSilently</tabstop>
+ <tabstop>deleteCheck</tabstop>
+ <tabstop>moveCheck</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/scheduler/Makefile.am b/plugins/scheduler/Makefile.am
new file mode 100644
index 0000000..b2c9760
--- /dev/null
+++ b/plugins/scheduler/Makefile.am
@@ -0,0 +1,36 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+kde_module_LTLIBRARIES = ktschedulerplugin.la
+noinst_HEADERS = schedulerplugin.h bwscheduler.h schedulerprefpagewidget.h \
+ bwsprefpagewidget.h bwswidget.h schedulerprefpage.h
+ktschedulerplugin_la_SOURCES = schedulerplugin.cpp \
+ schedulerpluginsettings.kcfgc bwscheduler.cpp schedulerprefpagewidget.cpp schedulerpage.ui bwspage.ui \
+ bwsprefpagewidget.cpp bwswidget.cpp schedulerprefpage.cpp
+
+# Libs needed by the plugin
+ktschedulerplugin_la_LIBADD = $(LIB_KHTML) $(LIB_KPARTS) \
+ ../../libktorrent/libktorrent.la \
+ $(LIB_QT) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KFILE)
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktschedulerplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+# rc file containing the GUI for the plugin
+# pluginsdir = $(kde_datadir)/ktschedulerplugin
+# plugins_DATA = ktschedulerpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+kde_services_DATA = ktschedulerplugin.desktop
+
+kde_kcfg_DATA = ktschedulerplugin.kcfg
+
+kticonsdir = $(kde_datadir)/ktorrent/icons
+
+kticons_DATA = cell-a-0000.png cell-a-0001.png cell-a-0002.png cell-a-0003.png\
+ cell-a-0004.png cell-b-0000.png cell-b-0001.png cell-b-0002.png\
+ cell-b-0003.png cell-b-0004.png
+rcdir = $(kde_datadir)/ktorrent
+rc_DATA = ktschedulerpluginui.rc
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/scheduler/bwscheduler.cpp b/plugins/scheduler/bwscheduler.cpp
new file mode 100644
index 0000000..56375b5
--- /dev/null
+++ b/plugins/scheduler/bwscheduler.cpp
@@ -0,0 +1,282 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "bwscheduler.h"
+#include "schedulerpluginsettings.h"
+
+#include <torrent/globals.h>
+#include <torrent/queuemanager.h>
+
+#include <interfaces/coreinterface.h>
+#include <interfaces/torrentinterface.h>
+
+#include <net/socketmonitor.h>
+
+#include <util/constants.h>
+#include <util/log.h>
+
+#include <qdatetime.h>
+#include <qfile.h>
+#include <qptrlist.h>
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+using namespace bt;
+
+namespace kt
+{
+ // ****** BWS *----------------------------------------------------------------------
+ BWS::BWS()
+ {
+ m_schedule = new ScheduleCategory*[7];
+ for(int i=0; i<7; ++i)
+ m_schedule[i] = new ScheduleCategory[24];
+
+ reset();
+ }
+
+ BWS& kt::BWS::operator=(const BWS& b)
+ {
+ for(int i=0; i<7; ++i)
+ for(int j=0; j<24; ++j)
+ m_schedule[i][j] = b.m_schedule[i][j];
+
+ for(int i=0; i<3; ++i)
+ {
+ download[i] = b.download[i];
+ upload[i] = b.upload[i];
+ }
+
+ return *this;
+ }
+
+ int BWS::getDownload(int cat)
+ {
+ return download[cat];
+ }
+
+ int BWS::getUpload(int cat)
+ {
+ return upload[cat];
+ }
+
+ void BWS::setDownload(int cat, int val)
+ {
+ download[cat] = val;
+ }
+
+ void BWS::setUpload(int cat, int val)
+ {
+ upload[cat] = val;
+ }
+
+ void BWS::setCategory(int day, int hour, ScheduleCategory val)
+ {
+ m_schedule[day][hour] = val;
+ }
+
+ ScheduleCategory BWS::getCategory(int day, int hour)
+ {
+ return m_schedule[day][hour];
+ }
+
+ void BWS::reset()
+ {
+ for(int i=0; i<7; ++i)
+ for(int j=0; j<24; ++j)
+ m_schedule[i][j] = CAT_NORMAL;
+
+ for(int i=0; i<3; ++i)
+ {
+ download[i] = 0;
+ upload[i] = 0;
+ }
+ }
+
+ BWS::~BWS()
+ {
+ for(int i=0; i<7; ++i)
+ delete [] m_schedule[i];
+
+ delete [] m_schedule;
+ }
+
+ void BWS::debug()
+ {
+ for(int i=0; i<7; ++i)
+ {
+ Log & lg = Out();
+ for(int j=0; j<24; ++j)
+ lg << m_schedule[i][j];
+ lg << endl;
+ }
+ }
+
+ // ---- BWScheduler --------------------------------------------------------------------
+
+ BWScheduler::BWScheduler()
+ : m_core(0)
+ {
+ m_enabled = SchedulerPluginSettings::enableBWS();
+ loadSchedule();
+ }
+
+ BWScheduler::~BWScheduler()
+ {
+ }
+
+ void BWScheduler::setSchedule(const BWS& sch)
+ {
+ Out(SYS_SCD|LOG_NOTICE) << "BWS: Setting new schedule..." << endl;
+ m_schedule = sch;
+ saveSchedule();
+ trigger();
+ }
+
+ void BWScheduler::setCoreInterface(CoreInterface* core)
+ {
+ m_core = core;
+ }
+
+ void BWScheduler::trigger()
+ {
+ if(!m_enabled)
+ return;
+
+ QDateTime now = QDateTime::currentDateTime();
+
+ QString prefix = QString("BWS: %1 :: ").arg(now.toString());
+
+ int t1 = now.date().dayOfWeek();
+ int t2 = now.time().hour();
+ ScheduleCategory sch = m_schedule.getCategory(t1-1, t2);
+
+ switch(sch)
+ {
+ case CAT_NORMAL:
+ Out(SYS_SCD|LOG_NOTICE) << prefix << "Switching to NORMAL category" << endl;
+ Out(SYS_SCD|LOG_NOTICE) << prefix << QString("%1 Up, %2 Down")
+ .arg(m_core->getMaxUploadSpeed()).arg(m_core->getMaxDownloadSpeed()) << endl;
+ if(!m_core)
+ break;
+ m_core->setPausedState(false);
+ net::SocketMonitor::setDownloadCap(1024 * m_core->getMaxDownloadSpeed());
+ net::SocketMonitor::setUploadCap(1024 * m_core->getMaxUploadSpeed());
+ break;
+ case CAT_FIRST:
+ Out(SYS_SCD|LOG_NOTICE) << prefix << "Switching to FIRST category" << endl;
+ Out(SYS_SCD|LOG_NOTICE) << prefix << QString("%1 Up, %2 Down")
+ .arg(m_schedule.getUpload(0)).arg(m_schedule.getDownload(0)) << endl;
+ if(!m_core)
+ break;
+ m_core->setPausedState(false);
+ net::SocketMonitor::setDownloadCap(1024 * m_schedule.getDownload(0));
+ net::SocketMonitor::setUploadCap(1024 * m_schedule.getUpload(0));
+ break;
+ case CAT_SECOND:
+ Out(SYS_SCD|LOG_NOTICE) << prefix << "Switching to SECOND category" << endl;
+ Out(SYS_SCD|LOG_NOTICE) << prefix << QString("%1 Up, %2 Down")
+ .arg(m_schedule.getUpload(1)).arg(m_schedule.getDownload(1)) << endl;
+ if(!m_core)
+ break;
+ m_core->setPausedState(false);
+ net::SocketMonitor::setDownloadCap(1024 * m_schedule.getDownload(1));
+ net::SocketMonitor::setUploadCap(1024 * m_schedule.getUpload(1));
+ break;
+ case CAT_THIRD:
+ Out(SYS_SCD|LOG_NOTICE) << prefix << "Switching to THIRD category" << endl;
+ Out(SYS_SCD|LOG_NOTICE) << prefix << QString("%1 Up, %2 Down")
+ .arg(m_schedule.getUpload(2)).arg(m_schedule.getDownload(2)) << endl;
+ if(!m_core)
+ break;
+ m_core->setPausedState(false);
+ net::SocketMonitor::setDownloadCap(1024 * m_schedule.getDownload(2));
+ net::SocketMonitor::setUploadCap(1024 * m_schedule.getUpload(2));
+ break;
+ case CAT_OFF:
+ Out(SYS_SCD|LOG_NOTICE) << prefix << "Switching to OFF" << endl;
+
+ if(!m_core)
+ break;
+ m_core->setPausedState(true);
+ break;
+ }
+ }
+
+ void BWScheduler::loadSchedule()
+ {
+ QFile file(KGlobal::dirs()->saveLocation("data","ktorrent") + "bwschedule");
+
+ if(!file.exists())
+ return;
+
+ file.open(IO_ReadOnly);
+ QDataStream stream(&file);
+
+ int tmp;
+
+ //extract category values
+ for(int i=0; i<3; ++i)
+ {
+ stream >> tmp;
+ m_schedule.setDownload(i, tmp);
+ stream >> tmp;
+ m_schedule.setUpload(i, tmp);
+ }
+
+ //extract schedule
+ for(int i=0; i<7; ++i)
+ {
+ for(int j=0; j<24; ++j)
+ {
+ stream >> tmp;
+ m_schedule.setCategory(i, j, (ScheduleCategory) tmp);
+ }
+ }
+
+ file.close();
+ }
+
+ void BWScheduler::saveSchedule()
+ {
+ QFile file(KGlobal::dirs()->saveLocation("data","ktorrent") + "bwschedule");
+
+ file.open(IO_WriteOnly);
+ QDataStream stream(&file);
+
+ for(int i=0; i<3; ++i)
+ {
+ stream << m_schedule.getDownload(i);
+ stream << m_schedule.getUpload(i);
+ }
+
+ //Now schedule
+ for(int i=0; i<7; ++i)
+ for(int j=0; j<24; ++j)
+ stream << (int) m_schedule.getCategory(i, j);
+
+ file.close();
+ }
+
+ void BWScheduler::setEnabled(bool theValue)
+ {
+ m_enabled = theValue;
+ }
+}
diff --git a/plugins/scheduler/bwscheduler.h b/plugins/scheduler/bwscheduler.h
new file mode 100644
index 0000000..e752b1d
--- /dev/null
+++ b/plugins/scheduler/bwscheduler.h
@@ -0,0 +1,173 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTBWSCHEDULER_H
+#define KTBWSCHEDULER_H
+
+#include <interfaces/coreinterface.h>
+
+namespace kt
+{
+
+ typedef enum bws_category
+ {
+ CAT_NORMAL,
+ CAT_FIRST,
+ CAT_SECOND,
+ CAT_THIRD,
+ CAT_OFF
+ }ScheduleCategory;
+
+ /**
+ * @author Ivan Vasic <ivasic@gmail.com>
+ * @brief This class represents bandwidth schedule for the week.
+ * It simplifies usage of 2 dimensional schedule (array) and its transfer between classes.
+ */
+ class BWS
+ {
+ ///Schedule
+ ScheduleCategory** m_schedule;
+
+ ///Download categories
+ int download[3];
+ ///Upload categories
+ int upload[3];
+
+ public:
+ BWS();
+ BWS& operator=(const BWS& b);
+ ~BWS();
+
+ /**
+ * @brief Resets this schedule.
+ */
+ void reset();
+
+
+ /**
+ * Gets download rate for category <i>cat</i>.
+ * @param cat download category index
+ * @return Download rate in KB/s
+ */
+ int getDownload(int cat);
+
+ /**
+ * Gets upload rate for category <i>cat</i>.
+ * @param cat upload category index
+ * @return upload rate in KB/s
+ */
+ int getUpload(int cat);
+
+
+ /**
+ * @brief Gets category for specified <i>day</i> and <i>hour</i>.
+ * @param day Number of day in a week.
+ * @param hour Hour of the day.
+ * @return ScheduleCategory - category associated with that day/hour.
+ */
+ ScheduleCategory getCategory(int day, int hour);
+
+
+ /**
+ * @brief Sets download rate for a category.
+ * @param cat Category to set rate to.
+ * @param val Download rate to set to category in KB/s
+ */
+ void setDownload(int cat, int val);
+
+ /**
+ * @brief Sets upload rate for a category.
+ * @param cat Category to set rate to.
+ * @param val Upload rate to set to category in KB/s
+ */
+ void setUpload(int cat, int val);
+
+
+ /**
+ * @brief Sets category for specified <i>day</i>/<i>hour</i> combination.
+ * @param day Day of the week.
+ * @param hour Hour of the day.
+ * @param val Category value.
+ */
+ void setCategory(int day, int hour, ScheduleCategory val);
+
+ ///Prints schedule to LogViewer. Used only for debugging.
+ void debug();
+ };
+
+
+ /**
+ * @brief Bandwidth scheduler class.
+ * @author Ivan Vasic <ivasic@gmail.com>
+ * Singleton class. Used to keep bandwidth schedule and change download/upload rate as necessary.
+ */
+ class BWScheduler
+ {
+ public:
+ inline static BWScheduler& instance()
+ {
+ static BWScheduler self;
+ return self;
+ }
+ ~BWScheduler();
+
+ /**
+ * Triggers bandwidth limit changes (if needed).
+ */
+ void trigger();
+
+ /**
+ * Sets a new schedule.
+ * @param sch - new BWS schedule.
+ * @note Call trigger() after setting new schedule for changes to take effect.
+ */
+ void setSchedule(const BWS& sch);
+
+ /**
+ * Sets a pointer to CoreInterface.
+ * Needed for getting global bandwidth limits.
+ * @param core Pointer to CoreInterface
+ */
+ void setCoreInterface(CoreInterface* core);
+
+ ///Pauses all torrents (TURN OFF category)
+ void pauseAll();
+
+ ///Loads schedule from HD
+ void loadSchedule();
+ ///Saves schedule to HD
+ void saveSchedule();
+
+ void setEnabled(bool theValue);
+
+
+ protected:
+ BWScheduler();
+ BWScheduler(const BWScheduler&);
+ BWScheduler& operator=(const BWScheduler&);
+
+ BWS m_schedule;
+ CoreInterface* m_core;
+
+ bool m_enabled;
+ };
+}
+
+#endif
+
diff --git a/plugins/scheduler/bwspage.ui b/plugins/scheduler/bwspage.ui
new file mode 100644
index 0000000..af0e771
--- /dev/null
+++ b/plugins/scheduler/bwspage.ui
@@ -0,0 +1,877 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>BWSPage</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>BWSPage</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>718</width>
+ <height>530</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>750</width>
+ <height>540</height>
+ </size>
+ </property>
+ <property name="caption">
+ <string>Bandwidth scheduler</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="resizeMode">
+ <enum>Fixed</enum>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout21</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Left click category</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton" row="1" column="1">
+ <property name="name">
+ <cstring>radio2</cstring>
+ </property>
+ <property name="text">
+ <string>Categor&amp;y 1</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>pix1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>pix2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>pix3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>pix5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="4" column="1">
+ <property name="name">
+ <cstring>radio5</cstring>
+ </property>
+ <property name="text">
+ <string>T&amp;urn off</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="1">
+ <property name="name">
+ <cstring>radio1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Normal</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>pix4</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="3" column="1">
+ <property name="name">
+ <cstring>radio4</cstring>
+ </property>
+ <property name="text">
+ <string>Category &amp;3</string>
+ </property>
+ <property name="accel">
+ <string>Alt+3</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="2" column="1">
+ <property name="name">
+ <cstring>radio3</cstring>
+ </property>
+ <property name="text">
+ <string>Category &amp;2</string>
+ </property>
+ <property name="accel">
+ <string>Alt+2</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1_2</cstring>
+ </property>
+ <property name="title">
+ <string>Right click category</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton" row="1" column="1">
+ <property name="name">
+ <cstring>radio22</cstring>
+ </property>
+ <property name="text">
+ <string>Category &amp;1</string>
+ </property>
+ <property name="accel">
+ <string>Alt+1</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>pix12</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>pix22</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>pix32</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>pix52</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="4" column="1">
+ <property name="name">
+ <cstring>radio52</cstring>
+ </property>
+ <property name="text">
+ <string>T&amp;urn off</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="1">
+ <property name="name">
+ <cstring>radio12</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Normal</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>pix42</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="3" column="1">
+ <property name="name">
+ <cstring>radio42</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Category 3</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="2" column="1">
+ <property name="name">
+ <cstring>radio32</cstring>
+ </property>
+ <property name="text">
+ <string>Category &amp;2</string>
+ </property>
+ <property name="accel">
+ <string>Alt+2</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="1">
+ <property name="name">
+ <cstring>groupCategories_2</cstring>
+ </property>
+ <property name="title">
+ <string>Categories</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel8_4</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;1. Category&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel8_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;3. Category&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6_5</cstring>
+ </property>
+ <property name="text">
+ <string>download:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>dlCat1</cstring>
+ </property>
+ <property name="maxValue">
+ <number>30000</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6_4_4</cstring>
+ </property>
+ <property name="text">
+ <string>upload:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>ulCat1</cstring>
+ </property>
+ <property name="maxValue">
+ <number>30000</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel9_4</cstring>
+ </property>
+ <property name="text">
+ <string>KB/s *</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>download:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>dlCat2</cstring>
+ </property>
+ <property name="maxValue">
+ <number>30000</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6_4_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>upload:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>ulCat2</cstring>
+ </property>
+ <property name="maxValue">
+ <number>30000</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel9_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>KB/s *</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="5" column="0">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>download:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>dlCat3</cstring>
+ </property>
+ <property name="maxValue">
+ <number>30000</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6_4_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>upload:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>ulCat3</cstring>
+ </property>
+ <property name="maxValue">
+ <number>30000</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel9_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>KB/s *</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel8_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;2. Category&lt;/b&gt;</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="kt::BWSWidget" row="0" column="0" rowspan="5" colspan="1">
+ <property name="name">
+ <cstring>m_bwsWidget</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>379</width>
+ <height>510</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>379</width>
+ <height>508</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="1">
+ <property name="name">
+ <cstring>layout34</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblStatus</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>* zero means no limit</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer16</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="4" column="1">
+ <property name="name">
+ <cstring>layout32</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout31</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnSave</cstring>
+ </property>
+ <property name="text">
+ <string>Save to file</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnLoad</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Load from file</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnReset</cstring>
+ </property>
+ <property name="text">
+ <string>Reset schedule</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>36</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>pix_icon</cstring>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer14</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>35</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout29</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnOk</cstring>
+ </property>
+ <property name="text">
+ <string>O&amp;k</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnApply</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Apply</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnCancel</cstring>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>kt::BWSWidget</class>
+ <header location="local">bwswidget.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image1</pixmap>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="4833">789c8597596f23470e80dfe75718c3b7c182e9eabb11ec834fc9873cbeaf601fc86ec9966df994cfc5fef794483633934d10c836fcb9582cde55fee5dbd2d9de68e9db2f5f9ee7349fb64bed153d2d7deb5e66b38fdffef3efff7ef99aa64b8baf2c5b4abffeebcb57dc5c6a9720499290640b8613e110ff22eb941b07657e7256f90be742e4a7c6a9ed076791c7a1732efbd784d3f817615a71167db0e55c0a17ce95c88f8ced3c7e74d6f306cea29fd159f583b3e8a75367d18f5bce8df0b63389be67e3ccfcd97716fd78e22cfaf1d459edefedcbedbc63e75af4cf85b33edeb8616cfef1b5b39eb7e72cf6c28b7166fb53678df7b5b3fadf388b3d503a8b3d40c6b9eaa35767d9cf37c6a5e53f73d6f3769d55bedf5f2789ac6bfcf2de1fda33ce4cdfbb716eeb6fce1acf1de3c2f2bbe7acf573605cdafe2b67f5efd359f20bb97165febe38b3c453fd2bfa7cd0a67165fea97c9db416ff1de3b1e9d77a6d12567bf8c159ed9d1ab7969fc2b8d378e2ba304579c93769bd719457fd524f2184cae221f51bd250a8fd3c35ae4cfec1b8b6fa06e3c6eaffc859d651f215b2d0c7efdab8327b969d459e9e8d7bfd57c68df587c43be4c1e20b685c1b9f0b17c1e283cfce927f96f911ca40d63fcbce7afebd31eb3a91b3fa37376ead9e64dea5759606a94f546eb23499883d9bc69932bef4acf22cf594725c1f8bfca9b3c8d3aa711e82c82bb73de3817111a4fe2171d67e1a1b97ba8e87ce5a8f12bfb48b2cfae8ceb85206894f9666135bff2e5c45d6fae894a3393a3f5be13aaeb7729eca4ff2d6ce3f5b701ef2b1c95f0aa7715dfb4de657519675d0fc8d8c9ba0f527f12faab234fd8fc695f1ab716dfa3f84ebb2089dec5f779678d381711d74beae398b7f540b3751bfd6e39ab1c9033b6bbd07e326687dcb3c2da8ecf47cfa301e9bbe37e389c94b3c0a2edba0f5f361dc194b3c8b36eed77acf9c453fab7f6d95587e6e8d83c957c25dc9668fdc5fc538b2c6e3a6e754e7adcc836252b6a9d69bccffb2ad535d67e98fb2ab538befaab3e8439947e5b84e82f6ffa17165f1bd7096fcc1ccb8b67a981b375a0f20f92d2775b078493dd54c75aaef0de9dfba75167fea2eb2f683dc5771388d753fbe396b7fca7c6b52ea2c5e57ce621fcbfdd26464f1812767ed8f63e3ced665de34c464fe9e1bb3d5b3f45bc3dca67a7fc9fba7697b46a9bf66cc13cb8fcc8b66c2960f3832b6f3988dad1e2828b799c53371d6f9766b9c5b7f4afe286983f5c3aeb3be177ace6c1ecc9cb51f6a678def8a7169f351fca710f5a93d9db3d64febacf9981aa76a2f5e3b6b7d5d1af7f6df3aebfb67e4acf93f72d6f972675cd83c7d77d67cac3bebfdfce9acf7eb87b3c66b665cdabc5feed9f44bfd51caade59395dbc4fc3f33cecc9f9b9e35bf786fdccfffc459e7ffc059eb7fe2acefcfd459df13dbceda5f4367bddffed8affdf0665c243a6f769c351f6367f51f7ad6fcefcf9dd57e72567fd959e3dd3a6bbc3b677daf34ce6affadb3dadbdb57263a5f46ce7adf8e9dd5dededfbe5e2f9cb5de5b678df7bb71a5fa59f767dccf77cd57d6f7136c185b7e79d359f3159cf53df7e8acf19e19f7f57ee5acef9b0d67f5b733b6fa844b63f38f07ceea9fcc3fca5b9bef58199b3dbce5acefa53567bd6fee8c73d54f95b3fa3b34b6fcc3b3b3f6dba1b3f6afe6a788fb6bad9f1f3f08f19b90b18ddff0f3dafefc2fe4bb284948f1b7f1e2e73fca4ff012af708ad77883b77f2f8f33bc8b9aeff1011ff1099f718e2ff88a6ff88e1ff8196da33fc92fe30aaee21aaee3060e70889bb885dbb88323dc8d7a407df941be8dd2dfa3ec5e94dac7033cc4233cc6133cc5333cc78bffb327c18029669863812556d1ef1a9ba8168080a1850ec608daaf30814bb882296ec035dc486426700b33b8837b78c0213cc2133ce367af3f7a93c01c5e700b5ee10d6fe11d093ee01396610556f104d6601d36a2d77abf0da2f41036610bb6b1841d18c12e7c873dd887033884233886133885b318297d3fc558e114cee1021208d1e01432c8a180122aa8e3c5190f2322c63bb507995aea62bc37698c9f34a14bba822d9ad235ddd02dcde80e87744f0f7fc823d1233de1363d634d737aa1577aa3ebf8047ba70ffaa4655aa1555aa375b37f0c298e6923ca0fb0a1212cd3266dd136edd08876e93bedd13e1dd0211d997e88f6031dd3099d624e67744e17f15f8b40296594cb273ef61632aa5ff34515d5d43032707c19449fd6e993db283b8217ee62fc0630fa31bf1cdf037c493bb1724ef98a162d30e56bcae185467cc3b731dfa0f935f919dff13daef1033ff2133fc77d039e73d41da55ff90d268b8afba17ea27d30a18edff9833f7999577895d7a88421aff31b6fc0e04ff5c6314acc031ef2266cc4c7ce036ff12ca66a10ff75d95eacfd2ccf3b0bfd38e651ac93f7d83b77f84e47d1e6f1222e0bf9c5ef3ff7a3f690766f4ffd0490aa85affffbf5cbef985d44a8</data>
+ </image>
+ <image name="image1">
+ <data format="PNG" length="1122">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042949444154388db5954d6c545514c77ff7de37eff1a6a550da994e5ba798868f948fc847a2a2911816c436b0a02ed0083161a1981877ee10d90aa94656063491882c5cd4c4b8103f20cd806909a98604da8482341de80cb69de9bcd799799d79efba980f1da3a80b4f727273939bdff99f9b93f3175a6b6a21849080020c4002a29a8f0a5dcd002803bed63a1035f0d0d0504b6f6fef51d7758fa4d3e98d8ee3fc03af128ee3303939399548242eb8aefb09300f78464de9f0f0f0d148cc7caf3d5424de97a7ec17104220242805520a44557bbd4b21f083801d33e63a6d1bc7bf19c6064e0319a35a58b9ae7ba43d54e4d7c54b64dc097c7f09d30ef04b36736983cc7c11b4456b9b492c5e20dc54a6540e00896a8268a7017010f8bcae1830d2e9f4c69ebe3c197782b2ce60d94ddc9b30f9feeb45ae5d7181c5ead35636ef08d3ffe26a76ee3208b4031a56d80aa00768fda362e9380e25bf80ef3b587613d7afc099533380a4b5dda46fcb5aa42998b9eb7173dce5e6788e434763ec7fc942532008ea436003660d2caa7f8d69c3bd8910674e4d033e83877ad8d36fb0aa4d6058658ac5358c5df6383bf480f31fdda5a3632bbbfb6da4ccd5c00a10b2012cc12fd97cfb5516f0187c25cee0619396480ec3ca909d0f3372d1e5f9fe66de3ad605587c712ec55256a2540d5519d19ae24a29057329839f7ecc138944d833102290f3184ae1e6d6f0e98739c61229e6d21eafbed1c9c8c5558c8f3ee4ceed76946a1c43d97091828585223e2eebfbc295f60d45de89707628cb58224577bc85a79e5d4d786581cddb9b0148259711b20145836221400416e0232d1f6595c9ce4538773ac7b54406f0b15784e8e80aa1ac2594190220d021e49f2437960156b70ba095e95f96f18acd8c263cc6122962b1167a7ba34cdd7ec8c977a699bed546722a8fc2a02b6e2185fff78ab586ce78894ddb9ab9f5738ed14b1e030756929d8ff2e4ae5544632b38f96e89a99b298ebd6950cc2d12ed0eb3618b26f520788462ad09877df60db600f0f1fb49c646f21c7ebd836dcf94e9d9b4c0db27d6d3bd36c6426a96bce731703046d7da129a4670836284a054869d4f1b1c7e2dca67676ef3c1898091ef5ad8bc358c3205c93bf7c9a40ad4c660f4728edd7bbb3142ea2fc1da711cfc200011e08b45f6bd1c261adbce9717928c5f9d63fc6a19f0014567773303073790f8214bd6f5282dfbb8b9fa1f6b40d7c1939393533b92e63ad9240990484aec7e41b27d579cbb531e0f67023492d86316ebb768ba1e0f786e6f37e56550c62237aee7011e54abd7c1412291b8a06de378b4d3c0b215da9708e5a3a442192085404a83d4accfecac067c0c43e13a70e3fa12e96409e0325000966bbb354465339d04a6f9dd15fe6dde07ce030780278088d05ad72cc9a6b2f2daaaa74d75a1f0e8d0d5f60b40868a8364ead6248430000b0857a126ffcdf396abf03ce089ffcb4c7f033046c6b4a995e7a00000000049454e44ae426082</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>btnOk</sender>
+ <signal>clicked()</signal>
+ <receiver>BWSPage</receiver>
+ <slot>btnOk_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnApply</sender>
+ <signal>clicked()</signal>
+ <receiver>BWSPage</receiver>
+ <slot>btnApply_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnSave</sender>
+ <signal>clicked()</signal>
+ <receiver>BWSPage</receiver>
+ <slot>btnSave_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnLoad</sender>
+ <signal>clicked()</signal>
+ <receiver>BWSPage</receiver>
+ <slot>btnLoad_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnReset</sender>
+ <signal>clicked()</signal>
+ <receiver>BWSPage</receiver>
+ <slot>btnReset_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>BWSPage</receiver>
+ <slot>reject()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>radio1</tabstop>
+ <tabstop>radio2</tabstop>
+ <tabstop>radio3</tabstop>
+ <tabstop>radio4</tabstop>
+ <tabstop>radio5</tabstop>
+ <tabstop>dlCat1</tabstop>
+ <tabstop>ulCat1</tabstop>
+ <tabstop>dlCat2</tabstop>
+ <tabstop>ulCat2</tabstop>
+ <tabstop>dlCat3</tabstop>
+ <tabstop>ulCat3</tabstop>
+</tabstops>
+<slots>
+ <slot>btnOk_clicked()</slot>
+ <slot>btnApply_clicked()</slot>
+ <slot>btnCancel_clicked()</slot>
+ <slot>btnSave_clicked()</slot>
+ <slot>btnLoad_clicked()</slot>
+ <slot>btnReset_clicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>bwswidget.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/scheduler/bwsprefpage.cpp b/plugins/scheduler/bwsprefpage.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/scheduler/bwsprefpage.cpp
diff --git a/plugins/scheduler/bwsprefpage.h b/plugins/scheduler/bwsprefpage.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/scheduler/bwsprefpage.h
diff --git a/plugins/scheduler/bwsprefpagewidget.cpp b/plugins/scheduler/bwsprefpagewidget.cpp
new file mode 100644
index 0000000..0c44a43
--- /dev/null
+++ b/plugins/scheduler/bwsprefpagewidget.cpp
@@ -0,0 +1,291 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "bwspage.h"
+#include "bwsprefpagewidget.h"
+#include "bwscheduler.h"
+#include "bwswidget.h"
+#include "schedulerpluginsettings.h"
+
+#include <knuminput.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+#include <kstdguiitem.h>
+#include <kpushbutton.h>
+
+#include <qcombobox.h>
+#include <qfile.h>
+#include <qdatastream.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+
+namespace kt
+{
+
+ /* PREF PAGE WIDGET --------------------------------------------------------*/
+
+ BWSPrefPageWidget::BWSPrefPageWidget(QWidget* parent, const char* name, WFlags fl)
+ : BWSPage(parent,name,fl)
+ {
+
+ loadDefault();
+
+ lblStatus->clear();
+
+ pix_icon->setPixmap(KGlobal::iconLoader()->loadIcon("clock",KIcon::NoGroup));
+
+ btnOk->setGuiItem(KStdGuiItem::ok());
+ btnCancel->setGuiItem(KStdGuiItem::cancel());
+ btnApply->setGuiItem(KStdGuiItem::apply());
+ btnSave->setGuiItem(KStdGuiItem::saveAs());
+ btnLoad->setGuiItem(KStdGuiItem::open());
+ btnReset->setGuiItem(KStdGuiItem::reset());
+
+
+ bool use_colors = SchedulerPluginSettings::useColors();
+
+ if(use_colors) //set up colors
+ {
+ pix1->setPaletteBackgroundColor(QColor(30,165,105));
+ pix2->setPaletteBackgroundColor(QColor(195,195,70));
+ pix3->setPaletteBackgroundColor(QColor(195,195,70));
+ pix4->setPaletteBackgroundColor(QColor(195,195,70));
+ pix5->setPaletteBackgroundColor(QColor(190,30,30));
+
+ pix12->setPaletteBackgroundColor(QColor(30,165,105));
+ pix22->setPaletteBackgroundColor(QColor(195,195,70));
+ pix32->setPaletteBackgroundColor(QColor(195,195,70));
+ pix42->setPaletteBackgroundColor(QColor(195,195,70));
+ pix52->setPaletteBackgroundColor(QColor(190,30,30));
+ } else //set up pixmaps
+ {
+ pix1->setPixmap(QPixmap(locate("data", QString("ktorrent/icons/cell-a-0000.png"))));
+ pix2->setPixmap(QPixmap(locate("data", QString("ktorrent/icons/cell-a-0001.png"))));
+ pix3->setPixmap(QPixmap(locate("data", QString("ktorrent/icons/cell-a-0002.png"))));
+ pix4->setPixmap(QPixmap(locate("data", QString("ktorrent/icons/cell-a-0003.png"))));
+ pix5->setPixmap(QPixmap(locate("data", QString("ktorrent/icons/cell-a-0004.png"))));
+
+ pix12->setPixmap(QPixmap(locate("data", QString("ktorrent/icons/cell-a-0000.png"))));
+ pix22->setPixmap(QPixmap(locate("data", QString("ktorrent/icons/cell-a-0001.png"))));
+ pix32->setPixmap(QPixmap(locate("data", QString("ktorrent/icons/cell-a-0002.png"))));
+ pix42->setPixmap(QPixmap(locate("data", QString("ktorrent/icons/cell-a-0003.png"))));
+ pix52->setPixmap(QPixmap(locate("data", QString("ktorrent/icons/cell-a-0004.png"))));
+ }
+
+ connect(radio1, SIGNAL(stateChanged(int)), this, SLOT(categoryChanged(int)));
+ connect(radio2, SIGNAL(stateChanged(int)), this, SLOT(categoryChanged(int)));
+ connect(radio3, SIGNAL(stateChanged(int)), this, SLOT(categoryChanged(int)));
+ connect(radio4, SIGNAL(stateChanged(int)), this, SLOT(categoryChanged(int)));
+ connect(radio5, SIGNAL(stateChanged(int)), this, SLOT(categoryChanged(int)));
+ connect(radio12, SIGNAL(stateChanged(int)), this, SLOT(categoryChanged(int)));
+ connect(radio22, SIGNAL(stateChanged(int)), this, SLOT(categoryChanged(int)));
+ connect(radio32, SIGNAL(stateChanged(int)), this, SLOT(categoryChanged(int)));
+ connect(radio42, SIGNAL(stateChanged(int)), this, SLOT(categoryChanged(int)));
+ connect(radio52, SIGNAL(stateChanged(int)), this, SLOT(categoryChanged(int)));
+
+ //pre-check default categories (say 1 for left and 0 for right)
+ radio2->setChecked(true);
+ radio12->setChecked(true);
+
+ m_bwsWidget->setSchedule(schedule);
+ }
+
+ BWSPrefPageWidget::~BWSPrefPageWidget()
+ {}
+
+ void BWSPrefPageWidget::btnSave_clicked()
+ {
+ QString sf = KFileDialog::getSaveFileName("/home","*",this,i18n("Choose a filename to save under"));
+
+ if(sf.isEmpty())
+ return;
+
+ saveSchedule(sf);
+ }
+
+ void BWSPrefPageWidget::btnLoad_clicked()
+ {
+ QString lf = KFileDialog::getOpenFileName("/home", "*",this,i18n("Choose a file"));
+
+ if(lf.isEmpty())
+ return;
+
+ btnReset_clicked();
+ loadSchedule(lf);
+ }
+
+ void BWSPrefPageWidget::saveSchedule(QString& fn)
+ {
+ schedule = m_bwsWidget->schedule();
+
+ QFile file(fn);
+
+ file.open(IO_WriteOnly);
+ QDataStream stream(&file);
+
+ //First category
+ stream << dlCat1->value();
+ stream << ulCat1->value();
+
+ //Second category
+ stream << dlCat2->value();
+ stream << ulCat2->value();
+
+ //Third category
+ stream << dlCat3->value();
+ stream << ulCat3->value();
+
+ //Now schedule
+ for(int i=0; i<7; ++i)
+ for(int j=0; j<24; ++j)
+ stream << (int) schedule.getCategory(i, j);
+
+ file.close();
+ lblStatus->setText(i18n("Schedule saved."));
+ }
+
+ void BWSPrefPageWidget::loadSchedule(QString& fn, bool showmsg)
+ {
+ QFile file(fn);
+
+ if(!file.exists()) {
+ if(showmsg)
+ KMessageBox::error(this, i18n("File not found."), i18n("Error"));
+ return;
+ }
+
+ file.open(IO_ReadOnly);
+ QDataStream stream(&file);
+
+ int tmp;
+
+ stream >> tmp;
+ dlCat1->setValue(tmp);
+ stream >> tmp;
+ ulCat1->setValue(tmp);
+
+ stream >> tmp;
+ dlCat2->setValue(tmp);
+ stream >> tmp;
+ ulCat2->setValue(tmp);
+
+ stream >> tmp;
+ dlCat3->setValue(tmp);
+ stream >> tmp;
+ ulCat3->setValue(tmp);
+
+ for(int i=0; i<7; ++i) {
+ for(int j=0; j<24; ++j) {
+ stream >> tmp;
+ schedule.setCategory(i, j, (ScheduleCategory) tmp);
+ }
+ }
+
+ file.close();
+
+ m_bwsWidget->setSchedule(schedule);
+ lblStatus->setText(i18n("Schedule loaded."));
+ }
+
+ void BWSPrefPageWidget::loadDefault()
+ {
+ //read schedule from HD
+ QString fn = KGlobal::dirs()->saveLocation("data","ktorrent") + "bwschedule";
+ loadSchedule(fn, false);
+ }
+
+ void BWSPrefPageWidget::btnReset_clicked()
+ {
+ schedule.reset();
+ m_bwsWidget->resetSchedule();
+
+ dlCat1->setValue(0);
+ dlCat2->setValue(0);
+ dlCat3->setValue(0);
+
+ ulCat1->setValue(0);
+ ulCat2->setValue(0);
+ ulCat3->setValue(0);
+
+ lblStatus->clear();
+ }
+
+ void BWSPrefPageWidget::apply()
+ {
+ schedule = m_bwsWidget->schedule();
+
+ SchedulerPluginSettings::writeConfig();
+
+ //update category values...
+ schedule.setDownload(0, dlCat1->value());
+ schedule.setUpload(0, ulCat1->value());
+ schedule.setDownload(1, dlCat2->value());
+ schedule.setUpload(1, ulCat2->value());
+ schedule.setDownload(2, dlCat3->value());
+ schedule.setUpload(2, ulCat3->value());
+
+ //set new schedule
+ BWScheduler::instance().setSchedule(schedule);
+ }
+
+ void BWSPrefPageWidget::btnOk_clicked()
+ {
+ apply();
+ accept();
+ }
+
+ void BWSPrefPageWidget::btnApply_clicked()
+ {
+ apply();
+ }
+}
+
+void kt::BWSPrefPageWidget::categoryChanged(int)
+{
+ if(radio1->isChecked())
+ m_bwsWidget->setLeftCategory(0);
+ else if(radio2->isChecked())
+ m_bwsWidget->setLeftCategory(1);
+ else if(radio3->isChecked())
+ m_bwsWidget->setLeftCategory(2);
+ else if(radio4->isChecked())
+ m_bwsWidget->setLeftCategory(3);
+ else if(radio5->isChecked())
+ m_bwsWidget->setLeftCategory(4);
+
+ if(radio12->isChecked())
+ m_bwsWidget->setRightCategory(0);
+ else if(radio22->isChecked())
+ m_bwsWidget->setRightCategory(1);
+ else if(radio32->isChecked())
+ m_bwsWidget->setRightCategory(2);
+ else if(radio42->isChecked())
+ m_bwsWidget->setRightCategory(3);
+ else if(radio52->isChecked())
+ m_bwsWidget->setRightCategory(4);
+}
+
+
+
+#include "bwsprefpagewidget.moc"
diff --git a/plugins/scheduler/bwsprefpagewidget.h b/plugins/scheduler/bwsprefpagewidget.h
new file mode 100644
index 0000000..ca0e80b
--- /dev/null
+++ b/plugins/scheduler/bwsprefpagewidget.h
@@ -0,0 +1,84 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTBWSPREFPAGEWIDGET_H
+#define KTBWSPREFPAGEWIDGET_H
+
+#include <qwidget.h>
+
+#include "bwspage.h"
+#include "bwscheduler.h"
+
+namespace kt
+{
+ /**
+ * @brief Bandwidth Scheduler page
+ * @author Ivan Vasic <ivasic@gmail.com>
+ */
+ class BWSPrefPageWidget : public BWSPage
+ {
+ Q_OBJECT
+ public:
+ BWSPrefPageWidget(QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
+ ~BWSPrefPageWidget();
+ /*$PUBLIC_FUNCTIONS$*/
+
+ /**
+ * @brief Loads default schedule.
+ * Default schedule is currently active (if enabled) and it's in ~/.kde/share/apps/ktorrent/bwschedule
+ */
+ void loadDefault();
+
+ /**
+ * Loads a schedule from HD.
+ * @param fn Schedule filename
+ * @param showmsg Should I show msgBox if file doesn't exist.
+ * @ref BWSPrefPageWidget::btnLoad_clicked()
+ * @ref BWSPrefPageWidget::loadDefault()
+ */
+ void loadSchedule(QString& fn, bool showmsg = true);
+
+ /**
+ * Saves current schedule to HD.
+ * @param fn Schedule filename.
+ */
+ void saveSchedule(QString& fn);
+
+
+ public slots:
+ /*$PUBLIC_SLOTS$*/
+ virtual void btnReset_clicked();
+ virtual void btnLoad_clicked();
+ virtual void btnSave_clicked();
+ virtual void btnApply_clicked();
+ virtual void btnOk_clicked();
+
+ private slots:
+ void categoryChanged(int);
+
+ ///Applies settings
+ void apply();
+
+ private:
+ BWS schedule;
+ };
+
+}
+
+#endif
diff --git a/plugins/scheduler/bwswidget.cpp b/plugins/scheduler/bwswidget.cpp
new file mode 100644
index 0000000..b430de3
--- /dev/null
+++ b/plugins/scheduler/bwswidget.cpp
@@ -0,0 +1,334 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "bwswidget.h"
+#include "schedulerpluginsettings.h"
+
+#include <klocale.h>
+#include <qstringlist.h>
+#include <qpixmap.h>
+#include <qtable.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qfont.h>
+#include <qcolor.h>
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+namespace kt
+{
+ BWSWidget::BWSWidget(QWidget* parent, const char* name, bool useColors)
+ : QTable(parent,name), m_leftCategory(1), m_rightCategory(0), draw_focus(true), right_click(false), use_colors(useColors)
+ {
+ use_colors = SchedulerPluginSettings::useColors();
+
+ QStringList days;
+ QStringList hours;
+
+ days << i18n("MON") << i18n("TUE") << i18n("WED") << i18n("THU") << i18n("FRI") << i18n("SAT") << i18n("SUN");
+ hours <<
+ "00:00-00:59h" << "01:00-01:59h" <<
+ "02:00-02:59h" << "03:00-03:59h" <<
+ "04:00-04:59h" << "05:00-05:59h" <<
+ "06:00-06:59h" << "07:00-07:59h" <<
+ "08:00-08:59h" << "09:00-09:59h" <<
+ "10:00-10:59h" << "11:00-11:59h" <<
+ "12:00-12:59h" << "13:00-13:59h" <<
+ "14:00-14:59h" << "15:00-15:59h" <<
+ "16:00-16:59h" << "17:00-17:59h" <<
+ "18:00-18:59h" << "19:00-19:59h" <<
+ "20:00-20:59h" << "21:00-21:59h" <<
+ "22:00-22:59h" << "23:00-23:59h";
+
+ insertRows(0,24);
+ insertColumns(0,7);
+
+ setColumnLabels(days);
+ setRowLabels(hours);
+
+ setFocusPolicy(QWidget::StrongFocus);
+
+ QFont f;
+ f.setPointSize(8);
+ setFont(f);
+
+ for(int i=0; i<7; ++i) {
+ setColumnWidth(i,40);
+ setColumnStretchable(i, true);
+ }
+ for(int i=0; i<24; ++i) {
+ setColumnWidth(i,40);
+ setRowStretchable(i, true);
+ }
+
+ setColumnMovingEnabled(false);
+ setRowMovingEnabled(false);
+
+ setSorting(false);
+
+ adjustSize();
+
+ for(int i=0; i<5; ++i) {
+ m_pix[i] = 0;
+ m_pixf[i] = 0;
+ m_color[i] = 0;
+ m_colorf[i] = 0;
+ }
+
+ setType(use_colors);
+
+ resetSchedule();
+
+ setSelectionMode(QTable::NoSelection);
+
+ setHScrollBarMode(QTable::AlwaysOff);
+ setVScrollBarMode(QTable::AlwaysOff);
+
+ connect(this, SIGNAL(currentChanged( int, int )), this, SLOT(cellSelectionChanged( int, int )));
+ connect(this, SIGNAL(pressed(int, int, int, const QPoint&)), this, SLOT(cellMouseDown(int, int, int, const QPoint& )));
+ }
+
+ BWSWidget::~BWSWidget()
+ {
+ for(int i=0; i<5; ++i) {
+ if(m_pix[i])
+ delete m_pix[i];
+ if(m_pixf[i])
+ delete m_pixf[i];
+ }
+ }
+
+ void BWSWidget::paintFocus(QPainter* p, const QRect& cr)
+ {
+ int x = rowAt(cr.y());
+ int y = columnAt(cr.x());
+
+ if(x == 1 && y == 1) {
+ int trt = 0;
+ ++trt;
+ }
+
+ if(lastFocused.x != x || lastFocused.y != y)
+ updateCell(lastFocused.x, lastFocused.y);
+
+ if(draw_focus) {
+
+ if(right_click)
+ drawCell(p, m_rightCategory, true);
+ else
+ drawCell(p, m_leftCategory, true);
+
+ lastFocused.x = x;
+ lastFocused.y = y;
+ } else {
+ lastFocused.x = -1;
+ lastFocused.y = -1;
+ }
+ }
+
+ void BWSWidget::cellSelectionChanged(int row, int col)
+ {
+ if(right_click)
+ setText(row, col, QString::number(m_rightCategory));
+ else
+ setText(row, col, QString::number(m_leftCategory));
+ }
+
+ void BWSWidget::paintCell(QPainter* p, int row, int col, const QRect& cr, bool selected)
+ {
+ if(selected)
+ return;
+
+ bool ok;
+ int cat = text(row,col).toInt(&ok);
+ if((ok || cat == 0) && cat >= 0 && cat <= 4)
+ drawCell(p, cat);
+ else
+ setText(row,col,QString::number(0));
+ // QTable::paintCell(p,row,col,cr,selected);
+ }
+
+ void BWSWidget::resetSchedule()
+ {
+ for(int i=0; i<7; ++i)
+ for(int j=0; j<24; ++j)
+ setText(j,i, "0");
+ draw_focus = false;
+ clearSelection();
+ updateHeaderStates();
+ }
+
+ void BWSWidget::repaintWidget()
+ {
+ for(int i=0; i<7; ++i)
+ for(int j=0; j<24; ++j)
+ updateCell(j,i);
+ }
+
+ void BWSWidget::cellMouseDown(int row, int col, int button, const QPoint& mousePos)
+ {
+ right_click = button == 2;
+ draw_focus = true;
+ cellSelectionChanged(row,col);
+ }
+
+ void BWSWidget::clearSelect()
+ {
+ draw_focus = false;
+ clearSelection();
+ updateHeaderStates();
+ repaintWidget();
+ }
+
+ void BWSWidget::setLeftCategory(const int& theValue)
+ {
+ m_leftCategory = theValue;
+ }
+
+ void BWSWidget::setRightCategory(const int& theValue)
+ {
+ m_rightCategory = theValue;
+ }
+
+
+ void BWSWidget::setUseColors(bool theValue)
+ {
+ use_colors = theValue;
+ }
+
+ void BWSWidget::drawCell(QPainter* p, int category, bool focus)
+ {
+ if(use_colors) {
+ if(focus) {
+ p->fillRect(0,0,40,20,*m_colorf[category]);
+ } else {
+ p->fillRect(0,0,40,20,*m_color[category]);
+ }
+
+ switch(category) {
+ case 0:
+ // p->drawText(QRect(0,0,40,20), Qt::AlignCenter | Qt::SingleLine, "normalllll");
+ break;
+ case 1:
+ case 2:
+ case 3:
+ p->drawText(QRect(0,0,40,20), Qt::AlignCenter | Qt::SingleLine, QString::number(category));
+ break;
+ case 4:
+ p->drawText(QRect(0,0,40,20), Qt::AlignCenter | Qt::SingleLine, "off");
+ break;
+ }
+
+ p->drawRect(0,0,40,20);
+ } else {
+ if(focus)
+ p->drawPixmap(0,0,*m_pixf[category]);
+ else
+ p->drawPixmap(0,0, *m_pix[category]);
+ }
+ }
+
+ void BWSWidget::setType(bool color)
+ {
+ if(color) {
+ for(int i=0; i<5; ++i) {
+ if(m_pix[i])
+ delete m_pix[i];
+ if(m_pixf[i])
+ delete m_pixf[i];
+
+ m_pix[i] = 0;
+ m_pixf[i] = 0;
+ }
+
+ m_color[0] = new QColor(30,165,105);
+ m_color[1] = new QColor(195,195,70);
+ m_color[2] = new QColor(195,195,70);
+ m_color[3] = new QColor(195,195,70);
+ m_color[4] = new QColor(190,30,30);
+
+ m_colorf[0] = new QColor(40,200,130);
+ m_colorf[1] = new QColor(210,220,130);
+ m_colorf[2] = new QColor(210,220,130);
+ m_colorf[3] = new QColor(210,220,130);
+ m_colorf[4] = new QColor(230,40,40);
+
+ } else {
+ for(int i=0; i<5; ++i) {
+ if(m_color[i])
+ delete m_color[i];
+ if(m_colorf[i])
+ delete m_colorf[i];
+
+ m_color[i] = 0;
+ m_colorf[i] = 0;
+ }
+
+ m_pix[0] = new QPixmap(locate("data", QString("ktorrent/icons/cell-a-0000.png")));
+ m_pix[1] = new QPixmap(locate("data", QString("ktorrent/icons/cell-a-0001.png")));
+ m_pix[2] = new QPixmap(locate("data", QString("ktorrent/icons/cell-a-0002.png")));
+ m_pix[3] = new QPixmap(locate("data", QString("ktorrent/icons/cell-a-0003.png")));
+ m_pix[4] = new QPixmap(locate("data", QString("ktorrent/icons/cell-a-0004.png")));
+
+ m_pixf[0] = new QPixmap(locate("data", QString("ktorrent/icons/cell-b-0000.png")));
+ m_pixf[1] = new QPixmap(locate("data", QString("ktorrent/icons/cell-b-0001.png")));
+ m_pixf[2] = new QPixmap(locate("data", QString("ktorrent/icons/cell-b-0002.png")));
+ m_pixf[3] = new QPixmap(locate("data", QString("ktorrent/icons/cell-b-0003.png")));
+ m_pixf[4] = new QPixmap(locate("data", QString("ktorrent/icons/cell-b-0004.png")));
+ }
+
+ use_colors = color;
+
+ repaintWidget();
+ }
+
+ void BWSWidget::setSchedule(const BWS& theValue)
+ {
+ m_schedule = theValue;
+ for(int i=0; i<7; ++i)
+ for(int j=0; j<24; ++j)
+ setText(j,i, QString::number((int) m_schedule.getCategory(i,j)));
+ }
+
+ const BWS& BWSWidget::schedule()
+ {
+ for(int i=0; i<7; ++i)
+ {
+ for(int j=0; j<24; ++j)
+ {
+ bool ok;
+ ScheduleCategory cat = (ScheduleCategory) text(j,i).toInt(&ok);
+
+ if((ok || cat == 0) && cat >= 0 && cat <= 4) //precaution
+ m_schedule.setCategory(i,j, cat);
+ else
+ m_schedule.setCategory(i,j,(ScheduleCategory) 0);
+ }
+ }
+
+ return m_schedule;
+ }
+
+ void BWSWidget::focusOutEvent(QFocusEvent* e)
+ {
+ if(e->lostFocus())
+ clearSelect();
+ }
+}
diff --git a/plugins/scheduler/bwswidget.h b/plugins/scheduler/bwswidget.h
new file mode 100644
index 0000000..c035edb
--- /dev/null
+++ b/plugins/scheduler/bwswidget.h
@@ -0,0 +1,107 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTBWSWIDGET_H
+#define KTBWSWIDGET_H
+
+#include <qtable.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qcolor.h>
+
+#include "bwscheduler.h"
+
+namespace kt
+{
+ typedef struct _focusedCell
+ {
+ int x;
+ int y;
+ }
+ FocusedCell;
+
+ /**
+ * @brief Bandwidth scheduler widget.
+ * @author Ivan Vasić <ivasic@gmail.com>
+ * This class is a QTable with customized cells. It's used for bandwidth scheduling by painting each cell with mouse moves or keyboard.
+ */
+ class BWSWidget : public QTable
+ {
+ Q_OBJECT
+ public:
+ BWSWidget(QWidget* parent = 0, const char* name = 0, bool useColors = true);
+ ~BWSWidget();
+
+ ///Repaints the whole widget
+ void repaintWidget();
+
+ ///Clears selection (removes focus)
+ void clearSelect();
+
+ ///Sets category for left mouse click.
+ void setLeftCategory(const int& theValue);
+ ///Sets category for right mouse click.
+ void setRightCategory(const int& theValue);
+
+
+ /**
+ * Sets cell paint type.
+ * @param color TRUE - paints colors. FALSE - paints pixmaps.
+ */
+ void setType(bool color);
+
+ ///Returns current schedule.
+ const BWS& schedule();
+ ///Sets schedule.
+ void setSchedule(const BWS& theValue);
+
+ public slots:
+ void resetSchedule();
+ void setUseColors(bool theValue);
+
+ private slots:
+ void cellSelectionChanged(int row, int col);
+ void cellMouseDown(int row, int col, int button, const QPoint& mousePos);
+
+ private:
+ void paintFocus(QPainter* p, const QRect& cr);
+ void paintCell(QPainter* p, int row, int col, const QRect& cr, bool selected);
+ void drawCell(QPainter* p, int category, bool focus = false);
+ void focusOutEvent(QFocusEvent*);
+
+ QPixmap* m_pix[5];
+ QPixmap* m_pixf[5];
+
+ QColor* m_color[5];
+ QColor* m_colorf[5];
+
+ int m_leftCategory;
+ int m_rightCategory;
+
+ FocusedCell lastFocused;
+
+ bool draw_focus;
+ bool right_click;
+
+ bool use_colors;
+
+ BWS m_schedule;
+ };
+}
+#endif
diff --git a/plugins/scheduler/cell-a-0000.png b/plugins/scheduler/cell-a-0000.png
new file mode 100644
index 0000000..c3a7afd
--- /dev/null
+++ b/plugins/scheduler/cell-a-0000.png
Binary files differ
diff --git a/plugins/scheduler/cell-a-0001.png b/plugins/scheduler/cell-a-0001.png
new file mode 100644
index 0000000..7f93d4e
--- /dev/null
+++ b/plugins/scheduler/cell-a-0001.png
Binary files differ
diff --git a/plugins/scheduler/cell-a-0002.png b/plugins/scheduler/cell-a-0002.png
new file mode 100644
index 0000000..635377e
--- /dev/null
+++ b/plugins/scheduler/cell-a-0002.png
Binary files differ
diff --git a/plugins/scheduler/cell-a-0003.png b/plugins/scheduler/cell-a-0003.png
new file mode 100644
index 0000000..16f5080
--- /dev/null
+++ b/plugins/scheduler/cell-a-0003.png
Binary files differ
diff --git a/plugins/scheduler/cell-a-0004.png b/plugins/scheduler/cell-a-0004.png
new file mode 100644
index 0000000..db91d39
--- /dev/null
+++ b/plugins/scheduler/cell-a-0004.png
Binary files differ
diff --git a/plugins/scheduler/cell-b-0000.png b/plugins/scheduler/cell-b-0000.png
new file mode 100644
index 0000000..1c765f0
--- /dev/null
+++ b/plugins/scheduler/cell-b-0000.png
Binary files differ
diff --git a/plugins/scheduler/cell-b-0001.png b/plugins/scheduler/cell-b-0001.png
new file mode 100644
index 0000000..2fdfcc2
--- /dev/null
+++ b/plugins/scheduler/cell-b-0001.png
Binary files differ
diff --git a/plugins/scheduler/cell-b-0002.png b/plugins/scheduler/cell-b-0002.png
new file mode 100644
index 0000000..6ed81c0
--- /dev/null
+++ b/plugins/scheduler/cell-b-0002.png
Binary files differ
diff --git a/plugins/scheduler/cell-b-0003.png b/plugins/scheduler/cell-b-0003.png
new file mode 100644
index 0000000..847b3a4
--- /dev/null
+++ b/plugins/scheduler/cell-b-0003.png
Binary files differ
diff --git a/plugins/scheduler/cell-b-0004.png b/plugins/scheduler/cell-b-0004.png
new file mode 100644
index 0000000..458aae4
--- /dev/null
+++ b/plugins/scheduler/cell-b-0004.png
Binary files differ
diff --git a/plugins/scheduler/ktschedulerplugin.desktop b/plugins/scheduler/ktschedulerplugin.desktop
new file mode 100644
index 0000000..c208cb3
--- /dev/null
+++ b/plugins/scheduler/ktschedulerplugin.desktop
@@ -0,0 +1,26 @@
+[Desktop Entry]
+Name=SchedulerPlugin
+Name[bg]=Приставка за график
+Name[cs]=Modul plánování
+Name[da]=Skemalægger-Plugin
+Name[de]=Planer-Modul
+Name[el]=Πρόσθετο προγραμματισμού
+Name[et]=Ajastamisplugin
+Name[it]=Plugin pianificatore
+Name[nb]=Planleggermodul
+Name[nds]=Planer-Moduul
+Name[nl]=Plannerplugin
+Name[pl]=Wtyczka planowanie transferu
+Name[pt_BR]=Plugin de agendamento
+Name[sk]=Scheduler Plugin
+Name[sr]=Прикључак распоређивања
+Name[sr@Latn]=Priključak raspoređivanja
+Name[sv]=Insticksprogram för schemaläggning
+Name[tr]=Zamanlayıcı Eklentisi
+Name[uk]=Втулок розкладу
+Name[xx]=xxSchedulerPluginxx
+Name[zh_CN]=带宽规划器插件
+Name[zh_TW]=排程器外掛程式
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktschedulerplugin
diff --git a/plugins/scheduler/ktschedulerplugin.kcfg b/plugins/scheduler/ktschedulerplugin.kcfg
new file mode 100644
index 0000000..7f752b3
--- /dev/null
+++ b/plugins/scheduler/ktschedulerplugin.kcfg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <kcfgfile name="ktschedulerpluginrc"/>
+ <group name="general">
+ <entry name="enableBWS" type="Bool">
+ <label>Enable bandwidth scheduler?</label>
+ <default>FALSE</default>
+ </entry>
+ <entry name="useColors" type="Bool">
+ <label>Use colors instead of pixmaps?</label>
+ <default>FALSE</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/scheduler/ktschedulerpluginui.rc b/plugins/scheduler/ktschedulerpluginui.rc
new file mode 100644
index 0000000..217e103
--- /dev/null
+++ b/plugins/scheduler/ktschedulerpluginui.rc
@@ -0,0 +1,12 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="ktorrent" version="1">
+ <MenuBar>
+ <Menu name="file">
+ <Action name="bwscheduler"/>
+ </Menu>
+ </MenuBar>
+
+ <ToolBar name="SchedulerToolbar" noMerge="1">
+ <Action name="bwscheduler"/>
+ </ToolBar>
+</kpartgui> \ No newline at end of file
diff --git a/plugins/scheduler/schedulerpage.ui b/plugins/scheduler/schedulerpage.ui
new file mode 100644
index 0000000..01a4f47
--- /dev/null
+++ b/plugins/scheduler/schedulerpage.ui
@@ -0,0 +1,146 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>SchedulerPage</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SchedulerPage</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>570</width>
+ <height>401</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Scheduler Plugin Pref Page</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>useBS</cstring>
+ </property>
+ <property name="text">
+ <string>Use &amp;bandwidth scheduler?</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBWS</cstring>
+ </property>
+ <property name="title">
+ <string>Bandwidth Scheduler</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>useColors</cstring>
+ </property>
+ <property name="text">
+ <string>Use colors instead of pi&amp;xmaps?</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>(Recommended for slower systems)</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="0">
+ <property name="name">
+ <cstring>btnEditBWS</cstring>
+ </property>
+ <property name="text">
+ <string>Edit s&amp;chedule</string>
+ </property>
+ </widget>
+ <spacer row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer22</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="1">
+ <property name="name">
+ <cstring>spacer18</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>80</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>useBS</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>groupBWS</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>btnEditBWS</sender>
+ <signal>clicked()</signal>
+ <receiver>SchedulerPage</receiver>
+ <slot>btnEditBWS_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>useColors</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SchedulerPage</receiver>
+ <slot>useColors_toggled(bool)</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>btnEditBWS_toggled(bool)</slot>
+ <slot>btnEditBWS_clicked()</slot>
+ <slot>useColors_toggled(bool)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/plugins/scheduler/schedulerplugin.cpp b/plugins/scheduler/schedulerplugin.cpp
new file mode 100644
index 0000000..7639293
--- /dev/null
+++ b/plugins/scheduler/schedulerplugin.cpp
@@ -0,0 +1,152 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+
+#include <interfaces/coreinterface.h>
+#include <interfaces/guiinterface.h>
+#include <util/constants.h>
+#include <util/log.h>
+
+#include <qstring.h>
+#include <qtimer.h>
+#include <qdatetime.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kstdaction.h>
+#include <kiconloader.h>
+#include <kglobal.h>
+
+#include "schedulerplugin.h"
+#include "schedulerpluginsettings.h"
+#include "bwscheduler.h"
+#include "schedulerprefpage.h"
+#include "bwsprefpagewidget.h"
+
+#include <torrent/downloadcap.h>
+#include <torrent/uploadcap.h>
+#include <torrent/globals.h>
+
+using namespace bt;
+
+K_EXPORT_COMPONENT_FACTORY(ktschedulerplugin,KGenericFactory<kt::SchedulerPlugin>("schedulerplugin"))
+
+namespace kt
+{
+ const QString NAME = "Bandwith Scheduler";
+ const QString AUTHOR = "Ivan Vasic";
+ const QString EMAIL = "ivasic@gmail.com";
+ const QString DESCRIPTION = i18n("Bandwidth scheduling plugin");
+
+ SchedulerPlugin::SchedulerPlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name, args,NAME,i18n("Bandwidth Scheduler"),AUTHOR,EMAIL,DESCRIPTION, "clock")
+ {
+ setXMLFile("ktschedulerpluginui.rc");
+ bws_action = 0;
+ connect(&m_timer, SIGNAL(timeout()), this, SLOT(timer_triggered()));
+ }
+
+
+ SchedulerPlugin::~SchedulerPlugin()
+ {
+ }
+
+ void SchedulerPlugin::load()
+ {
+ Pref = new SchedulerPrefPage(this);
+ getGUI()->addPrefPage(Pref);
+ BWScheduler::instance().setCoreInterface(getCore());
+
+ QDateTime now = QDateTime::currentDateTime();
+
+ //each hour
+ QDateTime hour = now.addSecs(3600);
+ QTime t(hour.time().hour(), 0);
+
+ //each minute
+// QDateTime hour = now.addSecs(60);
+// QTime t(hour.time().hour(), hour.time().minute());
+
+ QDateTime round(hour.date(), t);
+
+ // add a 5 second safety margin (BUG: 131246)
+ int secs_to = now.secsTo(round) + 5;
+
+ m_timer.start(secs_to*1000);
+
+ BWScheduler::instance().trigger();
+
+// updateEnabledBWS();
+ bws_action = new KAction(i18n("Open Bandwidth Scheduler" ), "clock", 0, this,
+ SLOT(openBWS()), actionCollection(), "bwscheduler" );
+ }
+
+ void SchedulerPlugin::unload()
+ {
+ getGUI()->removePrefPage(Pref);
+ if(Pref)
+ delete Pref;
+ Pref = 0;
+
+ if(bws_action)
+ delete bws_action;
+ bws_action = 0;
+
+ m_timer.stop();
+ }
+
+ void SchedulerPlugin::timer_triggered()
+ {
+ m_timer.changeInterval(3600*1000);
+ QDateTime now = QDateTime::currentDateTime();
+ BWScheduler::instance().trigger();
+ }
+
+ void SchedulerPlugin::openBWS()
+ {
+ if(SchedulerPluginSettings::enableBWS())
+ {
+ BWSPrefPageWidget dlg;
+ dlg.exec();
+ }
+ else
+ KMessageBox::sorry(0, i18n("Bandwidth scheduler is disabled. Go to Preferences->Scheduler to enable it."));
+ }
+
+ void SchedulerPlugin::updateEnabledBWS()
+ {
+ if(SchedulerPluginSettings::enableBWS())
+ {
+ bws_action = new KAction(i18n("Open Bandwidth Scheduler" ), "clock", 0, this,
+ SLOT(openBWS()), actionCollection(), "bwscheduler" );
+ }
+ else
+ {
+ if(bws_action)
+ delete bws_action;
+ bws_action = 0;
+ }
+ }
+
+ bool SchedulerPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+}
diff --git a/plugins/scheduler/schedulerplugin.h b/plugins/scheduler/schedulerplugin.h
new file mode 100644
index 0000000..6828d0d
--- /dev/null
+++ b/plugins/scheduler/schedulerplugin.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTschedulerPLUGIN_H
+#define KTschedulerPLUGIN_H
+
+#include <interfaces/plugin.h>
+
+#include "schedulerprefpage.h"
+
+#include <qtimer.h>
+
+#include <kstdaction.h>
+
+class QString;
+
+
+namespace kt
+{
+ class BWSPrefPage;
+
+ /**
+ * @author Ivan Vasic <ivasic@gmail.com>
+ * @brief KTorrent scheduler plugin.
+ *
+ */
+ class SchedulerPlugin : public Plugin
+ {
+ Q_OBJECT
+ public:
+ SchedulerPlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~SchedulerPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual bool versionCheck(const QString& version) const;
+
+ void updateEnabledBWS();
+
+ public slots:
+ void timer_triggered();
+ void openBWS();
+
+ private:
+ QTimer m_timer;
+
+ /* BANDWIDTH SCHEDULE PLUGIN */
+ SchedulerPrefPage* Pref;
+ KAction* bws_action;
+ };
+
+}
+
+#endif
diff --git a/plugins/scheduler/schedulerpluginsettings.kcfgc b/plugins/scheduler/schedulerpluginsettings.kcfgc
new file mode 100644
index 0000000..f38f816
--- /dev/null
+++ b/plugins/scheduler/schedulerpluginsettings.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=ktschedulerplugin.kcfg
+ClassName=SchedulerPluginSettings
+Namespace=kt
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables \ No newline at end of file
diff --git a/plugins/scheduler/schedulerprefpage.cpp b/plugins/scheduler/schedulerprefpage.cpp
new file mode 100644
index 0000000..fe82f34
--- /dev/null
+++ b/plugins/scheduler/schedulerprefpage.cpp
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "schedulerprefpage.h"
+#include "bwsprefpagewidget.h"
+#include "schedulerplugin.h"
+#include "schedulerpluginsettings.h"
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+
+
+namespace kt
+{
+
+ SchedulerPrefPage::SchedulerPrefPage(SchedulerPlugin* plugin)
+ : PrefPageInterface(i18n("Scheduler"), i18n("Scheduler plugin options"), KGlobal::iconLoader()->loadIcon("clock",KIcon::NoGroup)), m_plugin(plugin)
+ {
+ widget = 0;
+ }
+
+
+ SchedulerPrefPage::~SchedulerPrefPage()
+ {}
+
+ bool SchedulerPrefPage::apply()
+ {
+ widget->apply();
+// m_plugin->updateEnabledBWS();
+ return true;
+ }
+
+ void SchedulerPrefPage::createWidget( QWidget * parent )
+ {
+ widget = new SchedulerPrefPageWidget(parent);
+ }
+
+ void SchedulerPrefPage::updateData()
+ {}
+
+ void SchedulerPrefPage::deleteWidget()
+ {
+ delete widget;
+ widget = 0;
+ }
+}
diff --git a/plugins/scheduler/schedulerprefpage.h b/plugins/scheduler/schedulerprefpage.h
new file mode 100644
index 0000000..a10a98a
--- /dev/null
+++ b/plugins/scheduler/schedulerprefpage.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef KTSchedulerPREFPAGE_H
+#define KTSchedulerPREFPAGE_H
+
+#include <interfaces/prefpageinterface.h>
+
+#include "schedulerprefpagewidget.h"
+
+namespace kt
+{
+ class SchedulerPlugin;
+
+ /**
+ * @brief Scheduler Preferences Page.
+ * @author Ivan Vasic <ivasic@gmail.com>
+ */
+ class SchedulerPrefPage : public PrefPageInterface
+ {
+ public:
+ SchedulerPrefPage(SchedulerPlugin* plugin);
+ virtual ~SchedulerPrefPage();
+
+ virtual bool apply();
+ virtual void createWidget(QWidget* parent);
+ virtual void updateData();
+ virtual void deleteWidget();
+
+ private:
+ SchedulerPlugin* m_plugin;
+ SchedulerPrefPageWidget* widget;
+ };
+}
+
+#endif
diff --git a/plugins/scheduler/schedulerprefpagewidget.cpp b/plugins/scheduler/schedulerprefpagewidget.cpp
new file mode 100644
index 0000000..d6e73dd
--- /dev/null
+++ b/plugins/scheduler/schedulerprefpagewidget.cpp
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "schedulerprefpagewidget.h"
+#include "bwsprefpagewidget.h"
+#include "bwscheduler.h"
+#include "schedulerpluginsettings.h"
+
+#include <qcheckbox.h>
+#include <qtimer.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+
+namespace kt
+{
+
+ SchedulerPrefPageWidget::SchedulerPrefPageWidget(QWidget* parent, const char* name, WFlags fl)
+ : SchedulerPage(parent,name,fl)
+ {
+ groupBWS->setEnabled(false);
+ bool useit = SchedulerPluginSettings::enableBWS();
+ bool use_colors = SchedulerPluginSettings::useColors();
+ useBS->setChecked(useit);
+ useColors->setChecked(use_colors);
+ }
+
+
+ SchedulerPrefPageWidget::~SchedulerPrefPageWidget()
+ {}
+
+ void SchedulerPrefPageWidget::btnEditBWS_clicked()
+ {
+ BWSPrefPageWidget w(this);
+ w.exec();
+ }
+
+ void SchedulerPrefPageWidget::apply()
+ {
+ bool use_bws = useBS->isChecked();
+
+ SchedulerPluginSettings::setEnableBWS(use_bws);
+ SchedulerPluginSettings::setUseColors(useColors->isChecked());
+ SchedulerPluginSettings::writeConfig();
+
+ /* force trigger since the schedule has changed but after KTorrent::apply()
+ * Used QTimer with fixed interval - not very nice solution... */
+ if(useBS->isChecked())
+ QTimer::singleShot(1000, this, SLOT(scheduler_trigger()));
+
+ BWScheduler::instance().setEnabled(use_bws);
+ }
+
+ void SchedulerPrefPageWidget::scheduler_trigger()
+ {
+ BWScheduler::instance().trigger();
+ }
+
+ void SchedulerPrefPageWidget::useColors_toggled(bool)
+ {
+ SchedulerPluginSettings::setUseColors(useColors->isChecked());
+ SchedulerPluginSettings::writeConfig();
+ }
+
+}
+
+
+
diff --git a/plugins/scheduler/schedulerprefpagewidget.h b/plugins/scheduler/schedulerprefpagewidget.h
new file mode 100644
index 0000000..9f62c20
--- /dev/null
+++ b/plugins/scheduler/schedulerprefpagewidget.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ivan Vasić *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTSCHEDULERPREFPAGEWIDGET_H
+#define KTSCHEDULERPREFPAGEWIDGET_H
+
+#include <qwidget.h>
+#include "schedulerpage.h"
+
+namespace kt
+{
+ /**
+ * @brief Scheduler Preferences Page.
+ * @author Ivan Vasic <ivasic@gmail.com>
+ */
+ class SchedulerPrefPageWidget : public SchedulerPage
+ {
+ Q_OBJECT
+ public:
+ SchedulerPrefPageWidget(QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
+
+ ~SchedulerPrefPageWidget();
+
+ void apply();
+
+ public slots:
+ virtual void btnEditBWS_clicked();
+ void scheduler_trigger();
+ virtual void useColors_toggled(bool);
+ };
+
+}
+
+#endif
diff --git a/plugins/search/Makefile.am b/plugins/search/Makefile.am
new file mode 100644
index 0000000..af57f30
--- /dev/null
+++ b/plugins/search/Makefile.am
@@ -0,0 +1,28 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+kde_module_LTLIBRARIES = ktsearchplugin.la
+noinst_HEADERS = searchplugin.h searchprefpage.h searchtab.h searchenginelist.h
+ktsearchplugin_la_SOURCES = searchplugin.cpp htmlpart.cpp searchbar.ui \
+ searchpref.ui searchwidget.cpp searchprefpage.cpp searchpluginsettings.kcfgc \
+ searchtab.cpp searchenginelist.cpp
+
+# Libs needed by the plugin
+ktsearchplugin_la_LIBADD = ../../libktorrent/libktorrent.la \
+ $(LIB_KHTML) $(LIB_KPARTS) $(LIB_QT) \
+ $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KFILE)
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktsearchplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+# rc file containing the GUI for the plugin
+# pluginsdir = $(kde_datadir)/ktsearchplugin
+# plugins_DATA = ktsearchpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+kde_services_DATA = ktsearchplugin.desktop
+
+kde_kcfg_DATA = ktsearchplugin.kcfg
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/search/htmlpart.cpp b/plugins/search/htmlpart.cpp
new file mode 100644
index 0000000..b1c6b23
--- /dev/null
+++ b/plugins/search/htmlpart.cpp
@@ -0,0 +1,198 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kmessagebox.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+//#include <qfile.h>
+#include <qclipboard.h>
+#include <qapplication.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kfiledialog.h>
+#include <kparts/browserextension.h>
+#include <util/constants.h>
+#include <khtmlview.h>
+#include "htmlpart.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ HTMLPart::HTMLPart(QWidget *parent)
+ : KHTMLPart(parent)
+ {
+ setJScriptEnabled(true);
+ setJavaEnabled(true);
+ setMetaRefreshEnabled(true);
+ setPluginsEnabled(false);
+ setStatusMessagesEnabled(false);
+ KParts::BrowserExtension* ext = this->browserExtension();
+ connect(ext,SIGNAL(openURLRequest(const KURL&,const KParts::URLArgs&)),
+ this,SLOT(openURLRequest(const KURL&, const KParts::URLArgs& )));
+
+ ext->enableAction("copy",true);
+ ext->enableAction("paste",true);
+ active_job = 0;
+ }
+
+
+ HTMLPart::~HTMLPart()
+ {}
+
+ void HTMLPart::copy()
+ {
+ QString txt = selectedText();
+ QClipboard *cb = QApplication::clipboard();
+ // Copy text into the clipboard
+ if (cb)
+ cb->setText(txt,QClipboard::Clipboard);
+ }
+
+ void HTMLPart::openURLRequest(const KURL &u,const KParts::URLArgs &)
+ {
+ if (active_job)
+ {
+ active_job->kill(true);
+ active_job = 0;
+ }
+
+ KIO::TransferJob* j = KIO::get(u,false,false);
+ connect(j,SIGNAL(data(KIO::Job*,const QByteArray &)),
+ this,SLOT(dataRecieved(KIO::Job*, const QByteArray& )));
+ connect(j,SIGNAL(result(KIO::Job*)),this,SLOT(jobDone(KIO::Job* )));
+ connect(j,SIGNAL(mimetype(KIO::Job*, const QString &)),
+ this,SLOT(mimetype(KIO::Job*, const QString& )));
+
+ active_job = j;
+ curr_data.resize(0);
+ mime_type = QString::null;
+ curr_url = u;
+ }
+
+ void HTMLPart::back()
+ {
+ if (history.count() <= 1)
+ {
+ backAvailable(false);
+ }
+ else
+ {
+ history.pop_back();
+ KURL u = history.back();
+ openURL(u);
+ backAvailable(history.count() > 1 ? true : false);
+
+ }
+ }
+
+ void HTMLPart::addToHistory(const KURL & url)
+ {
+ history.append(url);
+ if (history.count() > 1)
+ backAvailable(true);
+ }
+
+ void HTMLPart::reload()
+ {
+ openURL(url());
+ }
+
+ void HTMLPart::dataRecieved(KIO::Job* job,const QByteArray & data)
+ {
+ if (job != active_job)
+ {
+ job->kill(true);
+ return;
+ }
+
+ if (data.size() == 0)
+ return;
+
+ Uint32 off = curr_data.size();
+ curr_data.resize(curr_data.size() + data.size());
+ for (Uint32 i = 0;i < data.size();i++)
+ {
+ curr_data[i + off] = data[i];
+ }
+ }
+
+ void HTMLPart::mimetype(KIO::Job* job,const QString & mt)
+ {
+ if (job != active_job)
+ {
+ job->kill(true);
+ return;
+ }
+
+ mime_type = mt;
+ }
+
+ void HTMLPart::jobDone(KIO::Job* job)
+ {
+ if (job != active_job)
+ {
+ job->kill(true);
+ return;
+ }
+
+ if (job->error() == 0)
+ {
+ bool is_bencoded_data = curr_data.size() > 0 &&
+ curr_data[0] == 'd' &&
+ curr_data[curr_data.size()-1] == 'e';
+
+ if (is_bencoded_data || mime_type == "application/x-bittorrent")
+ {
+ int ret = KMessageBox::questionYesNoCancel(0,
+ i18n("Do you want to download or save the torrent?"),
+ i18n("Download Torrent"),
+ KGuiItem(i18n("to download", "Download"),"down"),
+ KStdGuiItem::save());
+
+ if (ret == KMessageBox::Yes)
+ openTorrent(curr_url);
+ else if (ret == KMessageBox::No)
+ saveTorrent(curr_url);
+ }
+ else
+ {
+ addToHistory(curr_url);
+ begin(curr_url);
+ write(curr_data.data(),curr_data.size());
+ end();
+ view()->ensureVisible(0,0);
+ searchFinished();
+ }
+ }
+ else
+ {
+ begin(curr_url);
+ write(KIO::buildErrorString(job->error(),job->errorText()));/*,&curr_url));**/
+ end();
+ }
+ active_job = 0;
+ curr_data.resize(0);
+ curr_url = KURL();
+ mime_type = QString::null;
+ }
+}
+
+#include "htmlpart.moc"
diff --git a/plugins/search/htmlpart.h b/plugins/search/htmlpart.h
new file mode 100644
index 0000000..f5b78ee
--- /dev/null
+++ b/plugins/search/htmlpart.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef HTMLPART_H
+#define HTMLPART_H
+
+#include <khtml_part.h>
+
+namespace KIO
+{
+ class Job;
+}
+
+
+namespace kt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class HTMLPart : public KHTMLPart
+ {
+ Q_OBJECT
+ public:
+ HTMLPart(QWidget *parent = 0);
+ virtual ~HTMLPart();
+
+ public slots:
+ void back();
+ void reload();
+ void copy();
+ void openURLRequest(const KURL &url, const KParts::URLArgs &args);
+
+ private slots:
+ void addToHistory(const KURL & url);
+ void dataRecieved(KIO::Job* job,const QByteArray & data);
+ void mimetype(KIO::Job* job,const QString & mt);
+ void jobDone(KIO::Job* job);
+
+
+ signals:
+ void backAvailable(bool yes);
+ void openTorrent(const KURL & url);
+ void saveTorrent(const KURL & url);
+ void searchFinished();
+
+ private:
+ KURL::List history;
+ KIO::Job* active_job;
+ QByteArray curr_data;
+ QString mime_type;
+ KURL curr_url;
+ };
+}
+
+#endif
diff --git a/plugins/search/ktsearchplugin.desktop b/plugins/search/ktsearchplugin.desktop
new file mode 100644
index 0000000..0210135
--- /dev/null
+++ b/plugins/search/ktsearchplugin.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Name=SearchPlugin
+Name[bg]=Приставка за търсене
+Name[br]=Lugent klask
+Name[da]=SøgePlugin
+Name[de]=Suche-Modul
+Name[el]=Πρόσθετο αναζήτησης
+Name[es]=Complemento de búsqueda
+Name[et]=Otsimisplugin
+Name[fa]=وصلۀ جستجو
+Name[it]=Plugin di ricerca
+Name[nb]=Søkemodul
+Name[nds]=Söök-Moduul
+Name[nl]=Zoekplugin
+Name[pl]=Wtyczka wyszukiwania
+Name[pt]='Plugin' de Procura
+Name[pt_BR]=Plugin de Busca
+Name[sk]=Vyhľadávací Plugin
+Name[sr]=Прикључак претраге
+Name[sr@Latn]=Priključak pretrage
+Name[sv]=Sökinsticksprogram
+Name[tr]=Arama Eklentisi
+Name[uk]=Втулок пошуку
+Name[xx]=xxSearchPluginxx
+Name[zh_CN]=搜索插件
+Name[zh_TW]=搜尋外掛程式
+Comment=Search plugin for KTorrent
+Comment[ar]=قابس البحث لِــ KTorrent
+Comment[bg]=Приставка за търсене (KTorrent)
+Comment[br]=Lugent klask evit KTorrent
+Comment[ca]=Connector de cerca per a KTorrent
+Comment[cs]=Vyhledávací modul pro KTorrent
+Comment[da]=Søge-plugin for KTorrent
+Comment[de]=Suche-Modul für KTorrent
+Comment[el]=Πρόσθετο αναζήτησης για το KTorrent
+Comment[es]=Complemento de búsqueda para KTorrent
+Comment[et]=KTorrenti otsimisplugin
+Comment[fa]=وصلۀ جستجو برای KTorrent
+Comment[gl]=Plugin de procuras para KTorrent
+Comment[it]=Plugin di ricerca per KTorrent
+Comment[ja]=KTorrent のための検索プラグイン
+Comment[ka]=ძებნის მოდული KTorrent-თვის
+Comment[nb]=Søkemodul for KTorrent
+Comment[nds]=Söök-Moduul för KTorrent
+Comment[nl]=Zoekplugin voor KTorrent
+Comment[pl]=Wtyczka wyszukiwania dla KTorrent
+Comment[pt]='Plugin' de procura para o KTorrent
+Comment[pt_BR]=Busca de plug-in para o KTorrent
+Comment[sk]=Vyhľadávací plugin pre KTorrent
+Comment[sr]=Прикључак претраге за KTorrent
+Comment[sr@Latn]=Priključak pretrage za KTorrent
+Comment[sv]=Sökinsticksprogram för Ktorrent
+Comment[tr]=KTorrent için arama eklentisi
+Comment[uk]=Втулок пошуку для KTorrent
+Comment[xx]=xxSearch plugin for KTorrentxx
+Comment[zh_CN]=KTorrent 的搜索插件
+Comment[zh_TW]=KTorrent 搜尋外掛程式
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktsearchplugin
diff --git a/plugins/search/ktsearchplugin.kcfg b/plugins/search/ktsearchplugin.kcfg
new file mode 100644
index 0000000..fdcc141
--- /dev/null
+++ b/plugins/search/ktsearchplugin.kcfg
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <kcfgfile name="ktsearchpluginrc"/>
+ <group name="general">
+ <entry name="searchEngine" type="Int">
+ <label>Current search engine</label>
+ <default>0</default>
+ </entry>
+
+ <entry name="useDefaultBrowser" type ="Bool">
+ <label>Use default browser</label>
+ <default>true</default>
+ </entry>
+ <entry name="useCustomBrowser" type ="Bool">
+ <label>Use custom browser</label>
+ <default>false</default>
+ </entry>
+ <entry name="customBrowser" type ="String">
+ <label>Custom browser executable path</label>
+ <default>/usr/bin/firefox</default>
+ </entry>
+ <entry name="openInExternal" type = "Bool">
+ <default>false</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/search/searchbar.ui b/plugins/search/searchbar.ui
new file mode 100644
index 0000000..2b91ea0
--- /dev/null
+++ b/plugins/search/searchbar.ui
@@ -0,0 +1,99 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>SearchBar</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SearchBar</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>804</width>
+ <height>52</height>
+ </rect>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_back</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_reload</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_clear_button</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>m_search_text</cstring>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_search_button</cstring>
+ </property>
+ <property name="text">
+ <string>Search</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>60</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Search engine:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>m_search_engine</cstring>
+ </property>
+ </widget>
+ </hbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kcombobox.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/search/searchenginelist.cpp b/plugins/search/searchenginelist.cpp
new file mode 100644
index 0000000..cf7a11b
--- /dev/null
+++ b/plugins/search/searchenginelist.cpp
@@ -0,0 +1,134 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qstringlist.h>
+#include "searchenginelist.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ SearchEngineList::SearchEngineList()
+ {}
+
+
+ SearchEngineList::~SearchEngineList()
+ {}
+
+ void SearchEngineList::save(const QString& file)
+ {
+ QFile fptr(file);
+ if (!fptr.open(IO_WriteOnly))
+ return;
+
+ QTextStream out(&fptr);
+ out << "# PLEASE DO NOT MODIFY THIS FILE. Use KTorrent configuration dialog for adding new search engines." << ::endl;
+ out << "# SEARCH ENGINES list" << ::endl;
+
+ QValueList<SearchEngine>::iterator i = m_search_engines.begin();
+ while (i != m_search_engines.end())
+ {
+ SearchEngine & e = *i;
+
+ // replace spaces by %20
+ QString name = e.name;
+ name = name.replace(" ","%20");
+ QString u = e.url.prettyURL();
+ u = u.replace(" ","%20");
+ out << name << " " << u << ::endl;
+ i++;
+ }
+ }
+
+ void SearchEngineList::load(const QString& file)
+ {
+ m_search_engines.clear();
+
+ QFile fptr(file);
+
+ if(!fptr.exists())
+ makeDefaultFile(file);
+
+ if (!fptr.open(IO_ReadOnly))
+ return;
+
+ QTextStream in(&fptr);
+
+ int id = 0;
+
+ while (!in.atEnd())
+ {
+ QString line = in.readLine();
+
+ if(line.startsWith("#") || line.startsWith(" ") || line.isEmpty() ) continue;
+
+ QStringList tokens = QStringList::split(" ", line);
+
+ SearchEngine se;
+ se.name = tokens[0];
+ se.name = se.name.replace("%20"," ");
+ se.url = KURL::fromPathOrURL(tokens[1]);
+
+ for(Uint32 i=2; i<tokens.count(); ++i)
+ se.url.addQueryItem(tokens[i].section("=",0,0), tokens[i].section("=", 1, 1));
+
+ m_search_engines.append(se);
+ }
+ }
+
+ void SearchEngineList::makeDefaultFile(const QString& file)
+ {
+ QFile fptr(file);
+ if (!fptr.open(IO_WriteOnly))
+ return;
+
+ QTextStream out(&fptr);
+ out << "# PLEASE DO NOT MODIFY THIS FILE. Use KTorrent configuration dialog for adding new search engines." << ::endl;
+ out << "# SEARCH ENGINES list" << ::endl;
+ out << "KTorrents http://www.ktorrents.com/search.php?lg=0&sourceid=ktorrent&q=FOOBAR&f=0" << ::endl;
+ out << "bittorrent.com http://www.bittorrent.com/search_result.myt?search=FOOBAR" << ::endl;
+ out << "isohunt.com http://isohunt.com/torrents.php?ihq=FOOBAR&op=and" << ::endl;
+ out << "mininova.org http://www.mininova.org/search.php?search=FOOBAR" << ::endl;
+ out << "thepiratebay.org http://thepiratebay.org/search.php?q=FOOBAR" << ::endl;
+ out << "bitoogle.com http://bitoogle.com/search.php?q=FOOBAR" << ::endl;
+ out << "bytenova.org http://www.bitenova.org/search.php?search=FOOBAR&start=0&start=0&ie=utf-8&oe=utf-8" << ::endl;
+ out << "torrentspy.com http://torrentspy.com/search.asp?query=FOOBAR" << ::endl;
+ out << "torrentz.com http://www.torrentz.com/search_FOOBAR" << ::endl;
+ }
+
+ KURL SearchEngineList::getSearchURL(bt::Uint32 engine) const
+ {
+ if (engine >= m_search_engines.count())
+ return KURL();
+ else
+ return m_search_engines[engine].url;
+ }
+
+ QString SearchEngineList::getEngineName(bt::Uint32 engine) const
+ {
+ if (engine >= m_search_engines.count())
+ return QString::null;
+ else
+ return m_search_engines[engine].name;
+ }
+
+}
diff --git a/plugins/search/searchenginelist.h b/plugins/search/searchenginelist.h
new file mode 100644
index 0000000..236b8eb
--- /dev/null
+++ b/plugins/search/searchenginelist.h
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTSEARCHENGINELIST_H
+#define KTSEARCHENGINELIST_H
+
+#include <kurl.h>
+#include <qvaluelist.h>
+#include <util/constants.h>
+
+namespace kt
+{
+
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class SearchEngineList
+ {
+ struct SearchEngine
+ {
+ QString name;
+ KURL url;
+ };
+
+ QValueList<SearchEngine> m_search_engines;
+ public:
+ SearchEngineList();
+ virtual ~SearchEngineList();
+
+ void save(const QString& file);
+ void load(const QString& file);
+ void makeDefaultFile(const QString& file);
+
+ KURL getSearchURL(bt::Uint32 engine) const;
+ QString getEngineName(bt::Uint32 engine) const;
+
+ /// Get the number of engines
+ bt::Uint32 getNumEngines() const {return m_search_engines.count();}
+ };
+
+}
+
+#endif
diff --git a/plugins/search/searchplugin.cpp b/plugins/search/searchplugin.cpp
new file mode 100644
index 0000000..c674369
--- /dev/null
+++ b/plugins/search/searchplugin.cpp
@@ -0,0 +1,157 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kstdaction.h>
+#include <kpopupmenu.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <krun.h>
+#include <interfaces/guiinterface.h>
+#include "searchplugin.h"
+#include "searchwidget.h"
+#include "searchprefpage.h"
+#include "searchtab.h"
+#include "searchpluginsettings.h"
+#include "searchenginelist.h"
+
+
+#define NAME "Search"
+#define AUTHOR "Joris Guisson"
+#define EMAIL "joris.guisson@gmail.com"
+
+
+
+K_EXPORT_COMPONENT_FACTORY(ktsearchplugin,KGenericFactory<kt::SearchPlugin>("ktsearchplugin"))
+
+namespace kt
+{
+
+ SearchPlugin::SearchPlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name, args,NAME,i18n("Search"),AUTHOR,EMAIL,
+ i18n("Search for torrents on several popular torrent search engines"),"viewmag")
+ {
+ // setXMLFile("ktsearchpluginui.rc");
+ pref = 0;
+ tab = 0;
+ }
+
+
+ SearchPlugin::~SearchPlugin()
+ {}
+
+
+ void SearchPlugin::load()
+ {
+ engines.load(KGlobal::dirs()->saveLocation("data","ktorrent") + "search_engines");
+ KToolBar* tb = getGUI()->addToolBar("search");
+ tab = new SearchTab(tb);
+ connect(tab,SIGNAL(search( const QString&, int, bool )),
+ this,SLOT(search( const QString&, int, bool )));
+
+ pref = new SearchPrefPage(this);
+ getGUI()->addPrefPage(pref);
+ pref->updateData();
+ tab->updateSearchEngines(engines);
+ }
+
+ void SearchPlugin::unload()
+ {
+ tab->saveSettings();
+ SearchWidget* s = 0;
+ while ((s = searches.first()) != 0)
+ {
+ getGUI()->removeTabPage(s);
+ searches.removeFirst();
+ delete s;
+ }
+ getGUI()->removeToolBar(tab->getToolBar());
+ getGUI()->removePrefPage(pref);
+ delete pref;
+ pref = 0;
+ delete tab;
+ tab = 0;
+ }
+
+ void SearchPlugin::search(const QString & text,int engine,bool external)
+ {
+ if(external)
+ {
+ const SearchEngineList& sl = getSearchEngineList();
+
+ if (engine < 0 || engine >= sl.getNumEngines())
+ engine = 0;
+
+ QString s_url = sl.getSearchURL(engine).prettyURL();
+ s_url.replace("FOOBAR", KURL::encode_string(text), true);
+ KURL url = KURL::fromPathOrURL(s_url);
+
+ if(SearchPluginSettings::useDefaultBrowser())
+ kapp->invokeBrowser(url.url());
+ else
+ KRun::runCommand(QString("%1 \"%2\"").arg(SearchPluginSettings::customBrowser()).arg(url.url()), SearchPluginSettings::customBrowser(), "viewmag" );
+
+ return;
+ }
+
+ KIconLoader* iload = KGlobal::iconLoader();
+
+ SearchWidget* search = new SearchWidget(this);
+ getGUI()->addTabPage(search,iload->loadIconSet("viewmag", KIcon::Small),text,this);
+
+ KAction* copy_act = KStdAction::copy(search,SLOT(copy()),actionCollection());
+ copy_act->plug(search->rightClickMenu(),0);
+ searches.append(search);
+
+ search->updateSearchEngines(engines);
+ search->search(text,engine);
+ }
+
+ void SearchPlugin::preferencesUpdated()
+ {
+ engines.load(KGlobal::dirs()->saveLocation("data","ktorrent") + "search_engines");
+ if (tab)
+ tab->updateSearchEngines(engines);
+
+ for (QPtrList<SearchWidget>::iterator i = searches.begin(); i != searches.end();i++)
+ {
+ SearchWidget* w = *i;
+ w->updateSearchEngines(engines);
+ }
+ }
+
+ void SearchPlugin::tabCloseRequest(kt::GUIInterface* gui,QWidget* tab)
+ {
+ if (searches.contains((SearchWidget*)tab))
+ {
+ searches.remove((SearchWidget*)tab);
+ gui->removeTabPage(tab);
+ tab->deleteLater();
+ }
+ }
+
+ bool SearchPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+}
+#include "searchplugin.moc"
diff --git a/plugins/search/searchplugin.h b/plugins/search/searchplugin.h
new file mode 100644
index 0000000..ce65499
--- /dev/null
+++ b/plugins/search/searchplugin.h
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTSEARCHPLUGIN_H
+#define KTSEARCHPLUGIN_H
+
+#include <qptrlist.h>
+#include <interfaces/plugin.h>
+#include <interfaces/guiinterface.h>
+#include "searchenginelist.h"
+
+namespace kt
+{
+ class SearchWidget;
+ class SearchPrefPage;
+ class SearchTab;
+
+ /**
+ @author Joris Guisson
+ */
+ class SearchPlugin : public Plugin, public kt::CloseTabListener
+ {
+ Q_OBJECT
+ public:
+ SearchPlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~SearchPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual bool versionCheck(const QString& version) const;
+
+ void preferencesUpdated();
+
+ const SearchEngineList & getSearchEngineList() const {return engines;}
+ private slots:
+ void search(const QString & text,int engine,bool external);
+
+ private:
+ virtual void tabCloseRequest(kt::GUIInterface* gui,QWidget* tab);
+
+ private:
+ SearchPrefPage* pref;
+ SearchTab* tab;
+ SearchEngineList engines;
+ QPtrList<SearchWidget> searches;
+ };
+
+}
+
+#endif
diff --git a/plugins/search/searchpluginsettings.kcfgc b/plugins/search/searchpluginsettings.kcfgc
new file mode 100644
index 0000000..8b3488b
--- /dev/null
+++ b/plugins/search/searchpluginsettings.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=ktsearchplugin.kcfg
+ClassName=SearchPluginSettings
+Namespace=kt
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables
diff --git a/plugins/search/searchpref.ui b/plugins/search/searchpref.ui
new file mode 100644
index 0000000..8c8c026
--- /dev/null
+++ b/plugins/search/searchpref.ui
@@ -0,0 +1,320 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>SEPreferences</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SEPreferences</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>529</width>
+ <height>515</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>500</width>
+ <height>350</height>
+ </size>
+ </property>
+ <property name="caption">
+ <string>Search Preferences</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>External Browser</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>openExternal</cstring>
+ </property>
+ <property name="text">
+ <string>Open searches in external browser</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>useDefaultBrowser</cstring>
+ </property>
+ <property name="text">
+ <string>Use default browser</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout29</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>useCustomBrowser</cstring>
+ </property>
+ <property name="text">
+ <string>Custom browser path:</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>customBrowser</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer20</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox8</cstring>
+ </property>
+ <property name="title">
+ <string>Search Engines</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KActiveLabel">
+ <property name="name">
+ <cstring>m_infoLabel</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout22</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Search engine name:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>m_engine_name</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout23</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>URL:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>m_engine_url</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnAdd</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QListView">
+ <column>
+ <property name="text">
+ <string>Engines</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>URL</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_engines</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>50</height>
+ </size>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnRemove</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnRemoveAll</cstring>
+ </property>
+ <property name="text">
+ <string>R&amp;emove All</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer19</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btn_add_default</cstring>
+ </property>
+ <property name="text">
+ <string>Add Defau&amp;lt</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnUpdate</cstring>
+ </property>
+ <property name="text">
+ <string>Update From Internet</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>btnUpdate</sender>
+ <signal>clicked()</signal>
+ <receiver>SEPreferences</receiver>
+ <slot>btnUpdate_clicked()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>btnUpdate_clicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kactivelabel.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/search/searchprefpage.cpp b/plugins/search/searchprefpage.cpp
new file mode 100644
index 0000000..2285ead
--- /dev/null
+++ b/plugins/search/searchprefpage.cpp
@@ -0,0 +1,289 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson, Ivan Vasic *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kurl.h>
+#include <qtooltip.h>
+#include <qfile.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kiconloader.h>
+#include <kactivelabel.h>
+#include <kpushbutton.h>
+#include <klistview.h>
+#include <klineedit.h>
+#include <kmessagebox.h>
+#include <kio/netaccess.h>
+#include <klineedit.h>
+
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+
+#include <util/constants.h>
+#include "searchprefpage.h"
+#include "searchplugin.h"
+#include "searchenginelist.h"
+#include "searchpluginsettings.h"
+
+using namespace bt;
+
+namespace kt
+{
+ SearchPrefPageWidget::SearchPrefPageWidget(QWidget *parent) : SEPreferences(parent)
+ {
+ QString info = i18n("Use your web browser to search for the string %1"
+ " (capital letters) on the search engine you want to add. <br> "
+ "Then copy the URL in the addressbar after the search is finished, and paste it here.<br><br>Searching for %1"
+ " on Google for example, will result in http://www.google.com/search?q=FOOBAR&ie=UTF-8&oe=UTF-8. <br> "
+ "If you add this URL here, ktorrent can search using Google.").arg("FOOBAR").arg("FOOBAR");
+ QString info_short = i18n("Use your web browser to search for the string %1 (capital letters) "
+ "on the search engine you want to add. Use the resulting URL below.").arg("FOOBAR");
+ m_infoLabel->setText(info_short);
+ QToolTip::add(m_infoLabel,info);
+ QToolTip::add(m_engine_name,info);
+
+ connect(btnAdd, SIGNAL(clicked()), this, SLOT(addClicked()));
+ connect(btnRemove, SIGNAL(clicked()), this, SLOT(removeClicked()));
+ connect(btn_add_default, SIGNAL(clicked()), this, SLOT(addDefaultClicked()));
+ connect(btnRemoveAll, SIGNAL(clicked()), this, SLOT(removeAllClicked()));
+
+ connect(useCustomBrowser, SIGNAL(toggled(bool)), this, SLOT(customToggled( bool )));
+
+ useCustomBrowser->setChecked(SearchPluginSettings::useCustomBrowser());
+ useDefaultBrowser->setChecked(SearchPluginSettings::useDefaultBrowser());
+ customBrowser->setText(SearchPluginSettings::customBrowser());
+
+ customBrowser->setEnabled(useCustomBrowser->isChecked());
+ openExternal->setChecked(SearchPluginSettings::openInExternal());
+ }
+
+ void SearchPrefPageWidget::updateSearchEngines(const SearchEngineList & se)
+ {
+ m_engines->clear();
+
+ for (Uint32 i = 0;i < se.getNumEngines();i++)
+ {
+ new QListViewItem(m_engines,se.getEngineName(i),se.getSearchURL(i).prettyURL());
+ }
+ }
+
+ bool SearchPrefPageWidget::apply()
+ {
+ saveSearchEngines();
+
+ SearchPluginSettings::setUseCustomBrowser(useCustomBrowser->isChecked());
+ SearchPluginSettings::setUseDefaultBrowser(useDefaultBrowser->isChecked());
+ SearchPluginSettings::setCustomBrowser(customBrowser->text());
+ SearchPluginSettings::setOpenInExternal(openExternal->isChecked());
+ SearchPluginSettings::writeConfig();
+ return true;
+ }
+
+ void SearchPrefPageWidget::saveSearchEngines()
+ {
+ QFile fptr(KGlobal::dirs()->saveLocation("data","ktorrent") + "search_engines");
+ if (!fptr.open(IO_WriteOnly))
+ return;
+ QTextStream out(&fptr);
+ out << "# PLEASE DO NOT MODIFY THIS FILE. Use KTorrent configuration dialog for adding new search engines." << ::endl;
+ out << "# SEARCH ENGINES list" << ::endl;
+
+ QListViewItemIterator itr(m_engines);
+ while (itr.current())
+ {
+ QListViewItem* item = itr.current();
+ QString u = item->text(1);
+ QString name = item->text(0);
+ out << name.replace(" ","%20") << " " << u.replace(" ","%20") << endl;
+ itr++;
+ }
+ }
+
+ void SearchPrefPageWidget::addClicked()
+ {
+ if ( m_engine_url->text().isEmpty() || m_engine_name->text().isEmpty() )
+ {
+ KMessageBox::error(this, i18n("You must enter the search engine's name and URL"));
+ }
+ else if ( m_engine_url->text().contains("FOOBAR") )
+ {
+ KURL url = KURL::fromPathOrURL(m_engine_url->text());
+ if ( !url.isValid() )
+ {
+ KMessageBox::error(this, i18n("Malformed URL."));
+ return;
+ }
+
+ if (m_engines->findItem(m_engine_name->text(), 0))
+ {
+ KMessageBox::error(this, i18n("A search engine with the same name already exists. Please use a different name.")); return;
+ }
+
+ new QListViewItem(m_engines, m_engine_name->text(), m_engine_url->text());
+ m_engine_url->setText("");
+ m_engine_name->setText("");
+ }
+ else
+ {
+ KMessageBox::error(this, i18n("Bad URL. You should search for FOOBAR with your Internet browser and copy/paste the exact URL here."));
+ }
+ }
+
+ void SearchPrefPageWidget::removeClicked()
+ {
+ if ( m_engines->selectedItem() == 0 )
+ return;
+
+ QListViewItem* item = m_engines->selectedItem();
+ m_engines->takeItem(item);
+ delete item;
+ }
+
+ void SearchPrefPageWidget::addDefaultClicked()
+ {
+ QListViewItem* se = new QListViewItem(m_engines, "KTorrents", "http://www.ktorrents.com/search.php?lg=0&sourceid=ktorrent&q=FOOBAR&f=0");
+
+ se = new QListViewItem(m_engines, "bittorrent.com", "http://search.bittorrent.com/search.jsp?query=FOOBAR");
+
+ se = new QListViewItem(m_engines, "isohunt.com", "http://isohunt.com/torrents.php?ihq=FOOBAR&op=and");
+
+ se = new QListViewItem(m_engines, "mininova.org", "http://www.mininova.org/search.php?search=FOOBAR");
+
+ se = new QListViewItem(m_engines, "thepiratebay.org", "http://thepiratebay.org/search.php?q=FOOBAR");
+
+ se = new QListViewItem(m_engines, "bitoogle.com", "http://bitoogle.com/search.php?q=FOOBAR");
+
+ se = new QListViewItem(m_engines, "bytenova.org", "http://www.bitenova.org/search.php?search=FOOBAR&start=0&start=0&ie=utf-8&oe=utf-8");
+
+ se = new QListViewItem(m_engines, "torrentspy.com", "http://torrentspy.com/search.asp?query=FOOBAR");
+
+ se = new QListViewItem(m_engines, "torrentz.com", "http://www.torrentz.com/search_FOOBAR");
+ }
+
+ void SearchPrefPageWidget::removeAllClicked()
+ {
+ m_engines->clear();
+ }
+
+ void SearchPrefPageWidget::btnUpdate_clicked()
+ {
+ QString fn = KGlobal::dirs()->saveLocation("data","ktorrent") + "search_engines.tmp";
+ KURL source("http://www.ktorrent.org/downloads/search_engines");
+
+ if (KIO::NetAccess::download(source,fn,NULL))
+ {
+ //list successfully downloaded, remove temporary file
+ updateList(fn);
+ saveSearchEngines();
+ KIO::NetAccess::removeTempFile(fn);
+ }
+ }
+
+ void SearchPrefPageWidget::updateList(QString& source)
+ {
+ QFile fptr(source);
+
+ if (!fptr.open(IO_ReadOnly))
+ return;
+
+ QTextStream in(&fptr);
+
+ QMap<QString,KURL> engines;
+
+ while (!in.atEnd())
+ {
+ QString line = in.readLine();
+
+ if(line.startsWith("#") || line.startsWith(" ") || line.isEmpty() )
+ continue;
+
+ QStringList tokens = QStringList::split(" ", line);
+ QString name = tokens[0];
+ name = name.replace("%20"," ");
+
+ KURL url = KURL::fromPathOrURL(tokens[1]);
+ for(Uint32 i=2; i<tokens.count(); ++i)
+ url.addQueryItem(tokens[i].section("=",0,0), tokens[i].section("=", 1, 1));
+
+ engines.insert(name,url);
+ }
+
+ QMap<QString,KURL>::iterator i = engines.begin();
+ while (i != engines.end())
+ {
+ QListViewItem* item = m_engines->findItem(i.key(),0);
+ // if we have found the item, replace it if not make a new one
+ if (item)
+ item->setText(1, i.data().prettyURL());
+ else
+ new QListViewItem(m_engines,i.key(),i.data().prettyURL());
+
+ i++;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+
+
+ SearchPrefPage::SearchPrefPage(SearchPlugin* plugin)
+ : PrefPageInterface(i18n("a noun", "Search"), i18n("Search Engine Options"),
+ KGlobal::iconLoader()->loadIcon("viewmag",KIcon::NoGroup)), m_plugin(plugin)
+ {
+ widget = 0;
+ }
+
+
+ SearchPrefPage::~SearchPrefPage()
+ {}
+
+
+ bool SearchPrefPage::apply()
+ {
+ bool ret = widget->apply();
+ if(ret)
+ m_plugin->preferencesUpdated();
+
+ return ret;
+ }
+
+ void SearchPrefPage::createWidget(QWidget* parent)
+ {
+ widget = new SearchPrefPageWidget(parent);
+ }
+
+ void SearchPrefPage::deleteWidget()
+ {
+ delete widget;
+ }
+
+ void SearchPrefPage::updateData()
+ {
+ widget->updateSearchEngines(m_plugin->getSearchEngineList());
+
+ }
+
+ void SearchPrefPageWidget::customToggled(bool toggled)
+ {
+ customBrowser->setEnabled(toggled);
+ }
+}
+
+#include "searchprefpage.moc"
diff --git a/plugins/search/searchprefpage.h b/plugins/search/searchprefpage.h
new file mode 100644
index 0000000..a8d647d
--- /dev/null
+++ b/plugins/search/searchprefpage.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTSEARCHPREFPAGE_H
+#define KTSEARCHPREFPAGE_H
+
+
+#include <interfaces/prefpageinterface.h>
+#include "searchpref.h"
+
+#include <qstring.h>
+
+namespace kt
+{
+ class SearchPlugin;
+ class SearchEngineList;
+
+ class SearchPrefPageWidget : public SEPreferences
+ {
+ Q_OBJECT
+ public:
+ SearchPrefPageWidget(QWidget *parent = 0);
+
+ bool apply();
+ void saveSearchEngines();
+ void updateList(QString& source);
+
+ void updateSearchEngines(const SearchEngineList & se);
+
+ public slots:
+ virtual void btnUpdate_clicked();
+ void customToggled(bool toggled);
+
+ private slots:
+ void addClicked();
+ void removeClicked();
+ void addDefaultClicked();
+ void removeAllClicked();
+ };
+
+ /**
+ @author Joris Guisson
+ */
+ class SearchPrefPage : public PrefPageInterface
+ {
+ public:
+ SearchPrefPage(SearchPlugin* plugin);
+ virtual ~SearchPrefPage();
+
+ virtual bool apply();
+ virtual void createWidget(QWidget* parent);
+ virtual void updateData();
+ virtual void deleteWidget();
+
+ private:
+ SearchPrefPageWidget* widget;
+ SearchPlugin* m_plugin;
+ };
+
+}
+
+#endif
diff --git a/plugins/search/searchtab.cpp b/plugins/search/searchtab.cpp
new file mode 100644
index 0000000..1beaa6a
--- /dev/null
+++ b/plugins/search/searchtab.cpp
@@ -0,0 +1,169 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qapplication.h>
+#include <qlistbox.h>
+#include <qcheckbox.h>
+#include <kglobal.h>
+#include <kpushbutton.h>
+#include <kiconloader.h>
+#include <kcombobox.h>
+#include <kcompletion.h>
+#include <qlabel.h>
+#include <klocale.h>
+#include "searchtab.h"
+#include "searchenginelist.h"
+#include "searchpluginsettings.h"
+#include "functions.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ SearchTab::SearchTab(KToolBar* tb) : m_tool_bar(tb)
+ {
+ m_search_text = new KComboBox(tb);
+ m_search_text->setEditable(true);
+
+ m_clear_button = new KPushButton(tb);
+ m_search_new_tab = new KPushButton(i18n("Search"),tb);
+ m_search_engine = new KComboBox(tb);
+
+ m_clear_button->setIconSet(SmallIconSet(QApplication::reverseLayout() ? "clear_left" : "locationbar_erase"));
+ m_clear_button->setEnabled(false);
+
+ connect(m_search_new_tab,SIGNAL(clicked()),this,SLOT(searchNewTabPressed()));
+ connect(m_search_text,SIGNAL(returnPressed(const QString&)),this,SLOT(searchBoxReturn( const QString& )));
+ connect(m_search_text,SIGNAL(textChanged(const QString &)),this,SLOT(textChanged( const QString& )));
+ connect(m_clear_button,SIGNAL(clicked()),this,SLOT(clearButtonPressed()));
+ m_search_text->setMaxCount(20);
+ m_search_new_tab->setEnabled(false);
+ m_search_text->setInsertionPolicy(QComboBox::NoInsertion);
+
+ tb->insertWidget(1,-1,m_clear_button);
+ tb->insertWidget(2,-1,m_search_text);
+ tb->insertWidget(3,-1,m_search_new_tab);
+ tb->insertWidget(4,-1,new QLabel(i18n(" Engine: "),tb));
+ tb->insertWidget(5,-1,m_search_engine);
+ loadSearchHistory();
+ }
+
+ SearchTab::~SearchTab()
+ {
+ }
+
+ void SearchTab::saveSettings()
+ {
+ SearchPluginSettings::setSearchEngine(m_search_engine->currentItem());
+ SearchPluginSettings::writeConfig();
+ }
+
+ void SearchTab::updateSearchEngines(const SearchEngineList & sl)
+ {
+ int ci = 0;
+ if (m_search_engine->count() == 0)
+ ci = SearchPluginSettings::searchEngine();
+ else
+ ci = m_search_engine->currentItem();
+
+ m_search_engine->clear();
+ for (Uint32 i = 0;i < sl.getNumEngines();i++)
+ {
+ m_search_engine->insertItem(sl.getEngineName(i));
+ }
+ m_search_engine->setCurrentItem(ci);
+ }
+
+ void SearchTab::searchBoxReturn(const QString & str)
+ {
+ KCompletion *comp = m_search_text->completionObject();
+ if (!m_search_text->contains(str))
+ {
+ comp->addItem(str);
+ m_search_text->insertItem(str);
+ }
+ m_search_text->clearEdit();
+ saveSearchHistory();
+ search(str,m_search_engine->currentItem(),SearchPluginSettings::openInExternal());
+ }
+
+ void SearchTab::clearButtonPressed()
+ {
+ m_search_text->clearEdit();
+ }
+
+ void SearchTab::searchNewTabPressed()
+ {
+ searchBoxReturn(m_search_text->currentText());
+ }
+
+ void SearchTab::textChanged(const QString & str)
+ {
+ m_search_new_tab->setEnabled(str.length() > 0);
+ m_clear_button->setEnabled(str.length() > 0);
+ }
+
+ void SearchTab::loadSearchHistory()
+ {
+ QFile fptr(kt::DataDir() + "search_history");
+ if (!fptr.open(IO_ReadOnly))
+ return;
+
+ KCompletion *comp = m_search_text->completionObject();
+
+ Uint32 cnt = 0;
+ QTextStream in(&fptr);
+ while (!in.atEnd() && cnt < 50)
+ {
+ QString line = in.readLine();
+ if (line.isNull())
+ break;
+
+ if (!m_search_text->contains(line))
+ {
+ comp->addItem(line);
+ m_search_text->insertItem(line);
+ }
+ cnt++;
+ }
+
+ m_search_text->clearEdit();
+ }
+
+ void SearchTab::saveSearchHistory()
+ {
+ QFile fptr(kt::DataDir() + "search_history");
+ if (!fptr.open(IO_WriteOnly))
+ return;
+
+ QTextStream out(&fptr);
+ KCompletion *comp = m_search_text->completionObject();
+ QStringList items = comp->items();
+ for (QStringList::iterator i = items.begin();i != items.end();i++)
+ {
+ out << *i << endl;
+ }
+ }
+}
+
+#include "searchtab.moc"
+
diff --git a/plugins/search/searchtab.h b/plugins/search/searchtab.h
new file mode 100644
index 0000000..c392de9
--- /dev/null
+++ b/plugins/search/searchtab.h
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef SEARCHTAB_H
+#define SEARCHTAB_H
+
+#include <ktoolbar.h>
+
+class KComboBox;
+class KPushButton;
+
+namespace kt
+{
+ class SearchEngineList;
+
+ /**
+ Holds all widgets of the toolbar of the search plugin.
+ */
+ class SearchTab : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ SearchTab(KToolBar* toolbar);
+ virtual ~SearchTab();
+
+ /// Get the tool bar
+ KToolBar* getToolBar() {return m_tool_bar;}
+
+ /// Update the search engine list
+ void updateSearchEngines(const SearchEngineList & sl);
+
+ /// Save settings like current search engine
+ void saveSettings();
+
+ protected slots:
+ void clearButtonPressed();
+ void searchNewTabPressed();
+ void searchBoxReturn(const QString & str);
+ void textChanged(const QString & str);
+
+ signals:
+ /// Emitted when the user presses enter or clicks search
+ void search(const QString & text,int engine,bool external);
+
+ private:
+ void loadSearchHistory();
+ void saveSearchHistory();
+
+ private:
+ KToolBar* m_tool_bar;
+ KComboBox* m_search_text;
+ KComboBox* m_search_engine;
+ KPushButton* m_clear_button;
+ KPushButton* m_search_new_tab;
+ };
+}
+
+#endif
+
diff --git a/plugins/search/searchwidget.cpp b/plugins/search/searchwidget.cpp
new file mode 100644
index 0000000..2ed19e8
--- /dev/null
+++ b/plugins/search/searchwidget.cpp
@@ -0,0 +1,272 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson, Ivan Vasic *
+ * joris.guisson@gmail.com *
+ * ivasic@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <kapplication.h>
+#include <khtmlview.h>
+#include <qlayout.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <klineedit.h>
+#include <kpushbutton.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kiconloader.h>
+#include <kcombobox.h>
+#include <kpopupmenu.h>
+#include <kparts/partmanager.h>
+#include <kio/job.h>
+#include <kmessagebox.h>
+#include <kfiledialog.h>
+#include <kprogress.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <interfaces/guiinterface.h>
+#include <interfaces/coreinterface.h>
+#include "searchwidget.h"
+#include "searchbar.h"
+#include "htmlpart.h"
+#include "searchplugin.h"
+#include "searchenginelist.h"
+
+
+
+using namespace bt;
+
+namespace kt
+{
+
+
+ SearchWidget::SearchWidget(SearchPlugin* sp) : html_part(0),sp(sp)
+ {
+ QVBoxLayout* layout = new QVBoxLayout(this);
+ layout->setAutoAdd(true);
+ sbar = new SearchBar(this);
+ html_part = new HTMLPart(this);
+
+ right_click_menu = new KPopupMenu(this);
+ right_click_menu->insertSeparator();
+ back_id = right_click_menu->insertItem(
+ KGlobal::iconLoader()->loadIconSet(QApplication::reverseLayout()
+ ? "forward" : "back",KIcon::Small),
+ i18n("Back"),html_part,SLOT(back()));
+ right_click_menu->insertItem(
+ KGlobal::iconLoader()->loadIconSet("reload",KIcon::Small),
+ i18n("Reload"),html_part,SLOT(reload()));
+
+ right_click_menu->setItemEnabled(back_id,false);
+ sbar->m_back->setEnabled(false);
+ connect(sbar->m_search_button,SIGNAL(clicked()),this,SLOT(searchPressed()));
+ connect(sbar->m_clear_button,SIGNAL(clicked()),this,SLOT(clearPressed()));
+ connect(sbar->m_search_text,SIGNAL(returnPressed()),this,SLOT(searchPressed()));
+ connect(sbar->m_back,SIGNAL(clicked()),html_part,SLOT(back()));
+ connect(sbar->m_reload,SIGNAL(clicked()),html_part,SLOT(reload()));
+
+ sbar->m_clear_button->setIconSet(
+ KGlobal::iconLoader()->loadIconSet(QApplication::reverseLayout()
+ ? "clear_left" : "locationbar_erase",KIcon::Small));
+ sbar->m_back->setIconSet(
+ KGlobal::iconLoader()->loadIconSet(QApplication::reverseLayout()
+ ? "forward" : "back", KIcon::Small));
+ sbar->m_reload->setIconSet(
+ KGlobal::iconLoader()->loadIconSet("reload",KIcon::Small));
+
+
+ connect(html_part,SIGNAL(backAvailable(bool )),
+ this,SLOT(onBackAvailable(bool )));
+ connect(html_part,SIGNAL(onURL(const QString& )),
+ this,SLOT(onURLHover(const QString& )));
+ connect(html_part,SIGNAL(openTorrent(const KURL& )),
+ this,SLOT(onOpenTorrent(const KURL& )));
+ connect(html_part,SIGNAL(popupMenu(const QString&, const QPoint& )),
+ this,SLOT(showPopupMenu(const QString&, const QPoint& )));
+ connect(html_part,SIGNAL(searchFinished()),this,SLOT(onFinished()));
+ connect(html_part,SIGNAL(saveTorrent(const KURL& )),
+ this,SLOT(onSaveTorrent(const KURL& )));
+
+ KParts::PartManager* pman = html_part->partManager();
+ connect(pman,SIGNAL(partAdded(KParts::Part*)),this,SLOT(onFrameAdded(KParts::Part* )));
+
+ connect(html_part->browserExtension(),SIGNAL(loadingProgress(int)),this,SLOT(loadingProgress(int)));
+ prog = 0;
+ }
+
+
+ SearchWidget::~SearchWidget()
+ {
+ if (prog)
+ {
+ sp->getGUI()->removeProgressBarFromStatusBar(prog);
+ prog = 0;
+ }
+ }
+
+ void SearchWidget::updateSearchEngines(const SearchEngineList & sl)
+ {
+ int ci = sbar->m_search_engine->currentItem();
+ sbar->m_search_engine->clear();
+ for (Uint32 i = 0;i < sl.getNumEngines();i++)
+ {
+ sbar->m_search_engine->insertItem(sl.getEngineName(i));
+ }
+ sbar->m_search_engine->setCurrentItem(ci);
+ }
+
+ void SearchWidget::onBackAvailable(bool available)
+ {
+ sbar->m_back->setEnabled(available);
+ right_click_menu->setItemEnabled(back_id,available);
+ }
+
+ void SearchWidget::onFrameAdded(KParts::Part* p)
+ {
+ KHTMLPart* frame = dynamic_cast<KHTMLPart*>(p);
+ if (frame)
+ {
+ connect(frame,SIGNAL(popupMenu(const QString&, const QPoint& )),
+ this,SLOT(showPopupMenu(const QString&, const QPoint& )));
+ }
+ }
+
+ void SearchWidget::copy()
+ {
+ if (!html_part)
+ return;
+ html_part->copy();
+ }
+
+ void SearchWidget::search(const QString & text,int engine)
+ {
+ if (!html_part)
+ return;
+
+ if (sbar->m_search_text->text() != text)
+ sbar->m_search_text->setText(text);
+
+ if (sbar->m_search_engine->currentItem() != engine)
+ sbar->m_search_engine->setCurrentItem(engine);
+
+ const SearchEngineList & sl = sp->getSearchEngineList();
+
+ if (engine < 0 || (Uint32)engine >= sl.getNumEngines())
+ engine = sbar->m_search_engine->currentItem();
+
+ QString s_url = sl.getSearchURL(engine).prettyURL();
+ s_url.replace("FOOBAR", KURL::encode_string(text), true);
+ KURL url = KURL::fromPathOrURL(s_url);
+
+ statusBarMsg(i18n("Searching for %1...").arg(text));
+ //html_part->openURL(url);
+ html_part->openURLRequest(url,KParts::URLArgs());
+ }
+
+ void SearchWidget::searchPressed()
+ {
+ search(sbar->m_search_text->text(),sbar->m_search_engine->currentItem());
+ }
+
+ void SearchWidget::clearPressed()
+ {
+ sbar->m_search_text->clear();
+ }
+
+ void SearchWidget::onURLHover(const QString & url)
+ {
+ statusBarMsg(url);
+ }
+
+ void SearchWidget::onFinished()
+ {
+ }
+
+ void SearchWidget::onOpenTorrent(const KURL & url)
+ {
+ openTorrent(url);
+ }
+
+ void SearchWidget::onSaveTorrent(const KURL & url)
+ {
+ KFileDialog fdlg(QString::null,"*.torrent | " + i18n("torrent files"),this,0,true);
+ fdlg.setSelection(url.fileName());
+ fdlg.setOperationMode(KFileDialog::Saving);
+ if (fdlg.exec() == QDialog::Accepted)
+ {
+ KURL save_url = fdlg.selectedURL();
+ // start a copy job
+ KIO::Job* j = KIO::file_copy(url,save_url,-1,true);
+ // let it deal with the errors
+ j->setAutoErrorHandlingEnabled(true,0);
+ }
+ }
+
+ void SearchWidget::showPopupMenu(const QString & url,const QPoint & p)
+ {
+ right_click_menu->popup(p);
+ }
+
+ KPopupMenu* SearchWidget::rightClickMenu()
+ {
+ return right_click_menu;
+ }
+
+ void SearchWidget::onShutDown()
+ {
+ delete html_part;
+ html_part = 0;
+ }
+
+ void SearchWidget::statusBarMsg(const QString & url)
+ {
+ sp->getGUI()->changeStatusbar(url);
+ }
+
+ void SearchWidget::openTorrent(const KURL & url)
+ {
+ sp->getCore()->load(url);
+ }
+
+ void SearchWidget::loadingProgress(int perc)
+ {
+ if (perc < 100 && !prog)
+ {
+ prog = sp->getGUI()->addProgressBarToStatusBar();
+ if (prog)
+ prog->setValue(perc);
+ }
+ else if (prog && perc < 100)
+ {
+ prog->setValue(perc);
+ }
+ else if (perc == 100)
+ {
+ if (prog)
+ {
+ sp->getGUI()->removeProgressBarFromStatusBar(prog);
+ prog = 0;
+ }
+ statusBarMsg(i18n("Search finished"));
+ }
+ }
+}
+
+#include "searchwidget.moc"
diff --git a/plugins/search/searchwidget.h b/plugins/search/searchwidget.h
new file mode 100644
index 0000000..36fb73e
--- /dev/null
+++ b/plugins/search/searchwidget.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef BTSEARCHWIDGET_H
+#define BTSEARCHWIDGET_H
+
+#include <qwidget.h>
+#include <qvaluevector.h>
+#include <kurl.h>
+
+class SearchBar;
+class KProgress;
+class KPopupMenu;
+
+namespace KParts
+{
+ class Part;
+}
+
+namespace kt
+{
+ class HTMLPart;
+ class SearchPlugin;
+ class SearchEngineList;
+
+
+ /**
+ @author Joris Guisson
+
+ Widget which shows a KHTML window with the users search in it
+ */
+ class SearchWidget : public QWidget
+ {
+ Q_OBJECT
+ public:
+ SearchWidget(SearchPlugin* sp);
+ virtual ~SearchWidget();
+
+ KPopupMenu* rightClickMenu();
+
+ void updateSearchEngines(const SearchEngineList & sl);
+
+ public slots:
+ void search(const QString & text,int engine = 0);
+ void copy();
+ void onShutDown();
+
+ private slots:
+ void searchPressed();
+ void clearPressed();
+ void onURLHover(const QString & url);
+ void onFinished();
+ void onOpenTorrent(const KURL & url);
+ void onSaveTorrent(const KURL & url);
+ void showPopupMenu(const QString & s,const QPoint & p);
+ void onBackAvailable(bool available);
+ void onFrameAdded(KParts::Part* p);
+ void statusBarMsg(const QString & url);
+ void openTorrent(const KURL & url);
+ void loadingProgress(int perc);
+
+ private:
+ HTMLPart* html_part;
+ SearchBar* sbar;
+ KPopupMenu* right_click_menu;
+ int back_id;
+ SearchPlugin* sp;
+ KProgress* prog;
+ };
+
+}
+
+#endif
diff --git a/plugins/stats/ChartDrawer.cc b/plugins/stats/ChartDrawer.cc
new file mode 100644
index 0000000..75142e6
--- /dev/null
+++ b/plugins/stats/ChartDrawer.cc
@@ -0,0 +1,473 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "ChartDrawer.h"
+
+#ifdef USE_SOLARIS
+#include <ieeefp.h>
+int isinf(double x) { return !finite(x) && x==x; }
+#endif
+
+
+namespace kt {
+
+ChartDrawer::ChartDrawer(QWidget *p, wgtsize_t x_max, wgtsize_t y_max, bool autom, const QString & uname) : QWidget(p), mXMax(x_max), mYMax(y_max), mAutoMax(autom),
+ mUnitName(uname), mMMode(MaxModeExact)
+{
+ setBackgroundColor("white");
+}
+
+ChartDrawer::~ChartDrawer()
+{
+ QToolTip::remove(this);
+}
+
+ChartDrawer::wgtsize_t ChartDrawer::GetXMax() const
+{
+ return mXMax;
+}
+
+ChartDrawer::wgtsize_t ChartDrawer::GetYMax() const
+{
+ return mYMax;
+}
+
+void ChartDrawer::SetXMax(const wgtsize_t x)
+{
+ mXMax = x;
+
+ for(size_t i = 0; i < mEls.size(); i++)
+ {
+ mEls[i].pmVals -> resize(x, 0.0);
+ }
+}
+
+void ChartDrawer::SetYMax(const wgtsize_t y)
+{
+ mYMax = y;
+}
+
+inline ChartDrawer::wgtsize_t ChartDrawer::GetYScale() const
+{
+ return height() / 8;
+}
+
+
+inline ChartDrawer::wgtunit_t ChartDrawer::TrY(const ChartDrawer::wgtunit_t y) const
+{
+ return height() - y;
+}
+
+void ChartDrawer::paintEvent ( QPaintEvent *)
+{
+ QPainter pnt( this );
+
+ DrawScale(pnt);
+ DrawFrame(pnt);
+ DrawChart(pnt);
+
+}
+
+inline ChartDrawer::wgtunit_t ChartDrawer::height() const
+{
+ return QWidget::height() - 15;
+}
+
+inline ChartDrawer::wgtunit_t ChartDrawer::width() const
+{
+ return QWidget::width() - 65;
+}
+
+void ChartDrawer::DrawFrame(QPainter & rPnt )
+{
+ QPen op = rPnt.pen();
+ rPnt.setPen(QPen("#000", 3));
+
+ rPnt.drawLine(0, TrY(0), width()+3, TrY(0));
+ rPnt.drawLine(width()+1, TrY(0), width()+1, TrY(QWidget::height()));
+
+ QFont oldf(rPnt.font());
+ QFont newf(oldf);
+ newf.setWeight(QFont::Bold);
+ newf.setPointSize(10);
+ newf.setUnderline(1);
+
+ rPnt.setFont(newf);
+ rPnt.drawText(width() + 30, TrY(-7), mUnitName);
+ rPnt.setFont(oldf);
+
+ rPnt.setPen(op);
+}
+
+void ChartDrawer::DrawScale(QPainter & rPnt )
+{
+
+ if(!mYMax)
+ {
+ return;
+ }
+
+ QPen op = rPnt.pen();
+ QPen ep("#eee", 1, Qt::DashLine);
+ QPen lp("#666", 2, Qt::DotLine);
+ QPen tp("#000");
+
+ rPnt.setPen(ep);
+
+ for(wgtsize_t i = 1; i < width(); i += 10)
+ {
+ rPnt.drawLine(i, TrY(0), i, TrY(height()));
+ }
+
+ for(wgtsize_t i = 0; i < height(); i += 10)
+ {
+ rPnt.drawLine(0, TrY(i), width(), TrY(i));
+ }
+
+ rPnt.setPen(lp);
+ rPnt.drawLine(0, TrY(height() - 10), width(), TrY(height() - 10));
+ rPnt.setPen(tp);
+ rPnt.drawText(width() + 4, TrY(height() - 10) + 4, QString::number (mYMax));
+
+ for(wgtsize_t i = 0; i < height() - 15 ; i += GetYScale())
+ {
+ rPnt.setPen(lp);
+ rPnt.drawLine(0, TrY(i), width(), TrY(i));
+ rPnt.setPen(tp);
+ rPnt.drawText(width() + 4, TrY(i) + 4, QString::number ( (mYMax / 8.0 ) * ( i / static_cast<double>(GetYScale() )), 'f', 1 ) );
+ }
+
+ rPnt.setPen(op);
+}
+
+void ChartDrawer::DrawChart(QPainter & rPnt)
+{
+
+ QPen op = rPnt.pen();
+
+ uint32_t skip_max = 0;
+
+ for(size_t i = 0; i < mEls.size(); i++)
+ {
+ rPnt.setPen( *mEls[i].GetPen() );
+
+ for(size_t j = 1; j < mEls[i].pmVals -> size() - 1; j++)
+ {
+ rPnt.drawLine(
+ FindXScreenCoords(j-1),
+ TrY(FindYScreenCoords(mEls[i].pmVals -> at(j-1))),
+ FindXScreenCoords(j),
+ TrY(FindYScreenCoords(mEls[i].pmVals -> at(j)))
+ );
+ }
+//
+ rPnt.drawLine(
+ FindXScreenCoords(mEls[i].pmVals -> size() - 2),
+ TrY(FindYScreenCoords(mEls[i].pmVals -> at(mEls[i].pmVals -> size() - 2))),
+ width(),
+ TrY(FindYScreenCoords(mEls[i].pmVals -> at(mEls[i].pmVals -> size() - 1)))
+ );
+
+ // --------------------
+ // Line on top
+ // ------------
+ QPen myop(rPnt.pen());
+ QPen topl(myop);
+ topl.setStyle(Qt::DotLine);
+ rPnt.setPen(topl);
+ rPnt.drawLine(0, TrY(FindYScreenCoords(mEls[i].pmVals -> at(mEls[i].pmVals -> size() - 1))), width(), TrY(FindYScreenCoords(mEls[i].pmVals -> at(mEls[i].pmVals -> size() - 1))) );
+ rPnt.setPen(myop);
+
+ QFont oldf(rPnt.font());
+ QFont newf(oldf);
+ newf.setWeight(QFont::Bold);
+ newf.setPointSize(8);
+
+ rPnt.setFont(newf);
+ rPnt.drawText(5 + (i * 50), TrY(FindYScreenCoords(mEls[i].pmVals -> at(mEls[i].pmVals -> size() - 1))) + 11, QString::number (mEls[i].pmVals -> at(mEls[i].pmVals -> size() - 1), 'f', 2 ) );
+
+
+ //------------------
+ // max
+ //------------------
+
+ if(mMarkMax[i])
+ {
+ rPnt.setPen(topl);
+ std::pair<double, size_t> max = mEls[i] . Max();
+
+ rPnt.drawLine(
+ FindXScreenCoords(max.second), TrY(0), FindXScreenCoords(max.second), TrY(height())
+ );
+
+ rPnt.setPen(myop);
+
+ rPnt.setFont(newf);
+ QString maxv(QString::number (max.first, 'f', 2));
+
+ if(FindXScreenCoords(max.second) < 35)
+ {
+ rPnt.drawText(FindXScreenCoords(max.second) + 5, TrY(height() - (10 * (i - skip_max)) ) + 10, maxv ) ;
+ } else {
+ rPnt.drawText(FindXScreenCoords(max.second) - 35 , TrY(height() - (10 * (i - skip_max)) ) + 10, maxv ) ;
+ }
+ } else {
+ skip_max++;
+ }
+
+ rPnt.setFont(oldf);
+ rPnt.setPen(op);
+ }
+
+ rPnt.setPen(op);
+}
+
+inline ChartDrawer::wgtunit_t ChartDrawer::FindXScreenCoords(const double x) const
+{
+ return static_cast<wgtunit_t>((width() / static_cast<double>(mXMax)) * x) ;
+}
+
+inline ChartDrawer::wgtunit_t ChartDrawer::FindYScreenCoords(const double y) const
+{
+ return static_cast<wgtunit_t>(((height()) / static_cast<double>(mYMax)) * y) ;
+}
+
+void ChartDrawer::EnableAutoMax(bool a)
+{
+ mAutoMax = a;
+}
+
+void ChartDrawer::AddValue(const size_t idx, const double val, bool u )
+{
+
+ if( idx >= mEls.size() )
+ {
+ return;
+ }
+
+ ChartDrawerData::val_t::iterator it = mEls[idx].pmVals -> begin();
+
+ while(it != mEls[idx] .pmVals -> end() )
+ {
+ *it = *(it + 1);
+ it++;
+ }
+
+#ifdef USE_SOLARIS
+ if(isnand(val) || (isinf(val)))
+#else
+ if(std::isnan(val) || (std::isinf(val)))
+#endif
+ {
+ *(mEls[idx].pmVals -> end() -1) = 0.0;
+ } else {
+ *(mEls[idx].pmVals -> end() -1) = val;
+ }
+
+ if(mAutoMax)
+ {
+ if( (mMMode == MaxModeTop) && (val > mYMax) )
+ {
+ mYMax = static_cast<wgtsize_t>(val) + 3;
+
+ } else if(mMMode == MaxModeExact) {
+ FindSetMax();
+ }
+ }
+
+ if(u)
+ {
+ update();
+ }
+
+}
+
+void ChartDrawer::AddValues(ChartDrawerData Cdd, const bool max)
+{
+ if(Cdd.pmVals -> size() != mXMax)
+ {
+ Cdd.pmVals -> resize(mXMax, 0.0);
+ }
+
+ mEls.push_back(Cdd);
+ mMarkMax.push_back(max);
+
+ MakeLegendTooltip();
+}
+
+void ChartDrawer::AddValues(ChartDrawerData Cdd, const size_t idx, const bool max)
+{
+ if(Cdd.pmVals -> size() != mXMax)
+ {
+ Cdd.pmVals -> resize(mXMax, 0.0);
+ }
+
+ if(idx >= mEls.size())
+ {
+ mEls.push_back(Cdd);
+ } else {
+ mEls.insert(mEls.begin() + idx, Cdd);
+ }
+
+ if(idx >= mMarkMax.size())
+ {
+ mMarkMax.push_back(max);
+ } else {
+ mMarkMax.insert(mMarkMax.begin() + idx, max);
+ }
+
+ MakeLegendTooltip();
+}
+
+void ChartDrawer::AddValuesCnt(const QString & rN, const bool max)
+{
+ mEls.push_back(ChartDrawerData(mXMax, rN));
+ mMarkMax.push_back(max);
+
+ MakeLegendTooltip();
+}
+
+void ChartDrawer::AddValuesCnt(const QPen & rP, const QString & rN, const bool max)
+{
+ mEls.push_back(ChartDrawerData(rP, mXMax, rN));
+ mMarkMax.push_back(max);
+
+ MakeLegendTooltip();
+}
+
+void ChartDrawer::SetUnitName(const QString & rN)
+{
+ mUnitName = rN;
+}
+
+QString ChartDrawer::GetUnitName() const
+{
+ return mUnitName;
+}
+
+void ChartDrawer::mouseDoubleClickEvent ( QMouseEvent * evt )
+{
+ FindSetMax();
+
+ emit DoubleClicked(evt);
+}
+
+void ChartDrawer::EnableMaxDrawAt(const size_t at, const bool e)
+{
+ if(at >= mMarkMax.size())
+ {
+ return;
+ }
+
+ mMarkMax[at] = e;
+}
+
+void ChartDrawer::RemoveValuesCnt(const size_t idx)
+{
+ if(idx >= mEls.size())
+ {
+ return;
+ }
+
+ mEls.erase(mEls.begin() + idx);
+
+ if(idx <= mMarkMax.size())
+ {
+ mMarkMax.erase(mMarkMax.begin() + idx);
+ }
+
+ MakeLegendTooltip();
+
+}
+
+void ChartDrawer::Zero(const size_t idx)
+{
+ if(idx >= mEls.size())
+ {
+ return;
+ }
+
+ std::fill(mEls[idx].pmVals -> begin(), mEls[idx].pmVals -> end(), 0.0);
+
+ if(mAutoMax)
+ {
+ mYMax = 1;
+ }
+}
+
+void ChartDrawer::MakeLegendTooltip()
+{
+ QToolTip::remove(this);
+
+ QString helpstr(QString("<b>%1:</b><br><br>").arg(i18n("Legend")));
+ QMimeSourceFactory* factory = QMimeSourceFactory::defaultFactory();
+ std::vector<QImage> img;
+
+ for(size_t i = 0; i < mEls.size(); i++)
+ {
+ img.push_back(QImage(16,16, 32));
+ img[i].fill(mEls[i].GetPen() -> color().pixel());
+
+ for(uint8_t px = 0; px < 16; px++)
+ {
+ img[i].setPixel(px, 0, 0); //t
+ img[i].setPixel(0, px, 0); //l
+ img[i].setPixel(px, 15, 0); //b
+ img[i].setPixel(15, px, 0); //r
+ }
+
+ factory->setImage(mEls[i].GetName().replace(' ', '_') + "-" + QString::number(i), img[i]);
+ helpstr += QString("<img src='%1'>&nbsp;&nbsp;-&nbsp;&nbsp;%2<br>").arg(mEls[i].GetName().replace(" ", "_") + "-" + QString::number(i)).arg( mEls[i].GetName() );
+ }
+
+ QToolTip::add(this, helpstr);
+}
+
+void ChartDrawer::FindSetMax()
+{
+ wgtsize_t mymax = 1;
+
+ for(val_t::const_iterator it = mEls.begin(); it != mEls.end(); ++it)
+ {
+ for(ChartDrawerData::val_t::const_iterator subit = it -> pmVals -> begin(); subit != it -> pmVals -> end(); ++subit)
+ {
+ if ( (*subit) > mymax )
+ {
+ mymax = static_cast<wgtsize_t>(*subit) + 3;
+ }
+ }
+ }
+
+ mYMax = mymax;
+}
+
+void ChartDrawer::SetMaxMode(const MaxMode mm)
+{
+ mMMode = mm;
+}
+
+ChartDrawer::MaxMode ChartDrawer::GetMaxMode() const
+{
+ return mMMode;
+}
+
+} //NS end
+
+#include "ChartDrawer.moc"
diff --git a/plugins/stats/ChartDrawer.h b/plugins/stats/ChartDrawer.h
new file mode 100644
index 0000000..741240e
--- /dev/null
+++ b/plugins/stats/ChartDrawer.h
@@ -0,0 +1,281 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef CHARTDRAWER_H_
+#define CHARTDRAWER_H_
+
+#include <qwidget.h>
+#include <qpainter.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qmime.h>
+#include <qimage.h>
+
+#include <klocale.h>
+
+#include <vector>
+#include <cmath>
+#include <algorithm> //fill
+
+#include "ChartDrawerData.h"
+
+namespace kt {
+
+/**
+\brief Widget for drawing line charts
+\author Krzysztof Kundzicz <athantor@gmail.com>
+*/
+class ChartDrawer : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ ///Type used as widget size unit
+ typedef uint32_t wgtsize_t;
+ ///Type used as unit in chart
+ typedef int64_t wgtunit_t;
+ /**
+ \brief Type used for data storing
+ \sa ChartDrawerData
+ */
+ typedef std::vector<ChartDrawerData> val_t;
+ ///Determines max mode
+ enum MaxMode { MaxModeTop, MaxModeExact };
+
+ private:
+ ///Maximum X value
+ wgtsize_t mXMax;
+ ///Maximum Y value
+ wgtsize_t mYMax;
+ ///Auto maximum setting
+ bool mAutoMax;
+ ///Chart data
+ val_t mEls;
+ ///Name of the chart unit
+ QString mUnitName;
+ ///Mark max
+ std::vector<bool> mMarkMax;
+ ///Max mode
+ MaxMode mMMode;
+
+ ///Paint event handler
+ void paintEvent ( QPaintEvent * );
+ /**
+ \brief Draws chart's frame
+ \param rPnt Painter on which things will be drawn
+ */
+ void DrawFrame(QPainter &rPnt);
+ /**
+ \brief Draws chart's scale
+ \param rPnt Painter on which things will be drawn
+ */
+ void DrawScale(QPainter &rPnt);
+ /**
+ \brief Draws chart
+ \param rPnt Painter on which things will be drawn
+ */
+ void DrawChart(QPainter &rPnt);
+
+ /**
+ \brief Gets distance between two values on OY
+ \return Distance
+ */
+ inline wgtsize_t GetYScale() const;
+
+ /**
+ \brief Translates widget Y coord to cartesian
+ \param y Coord
+ \return Coord
+ */
+ inline wgtunit_t TrY(const wgtunit_t y) const;
+ /**
+ \brief Returns charts height
+ \return Height
+
+ Return only height of the chart's inside the frame — not the whole widget's
+ */
+ inline wgtunit_t height() const;
+ /**
+ \brief Returns charts width
+ \return Width
+
+ Return only width of the chart's inside the frame — not the whole widget's
+ */
+ inline wgtunit_t width() const;
+
+ /**
+ \brief Finds screen X coord on the widget
+ \param x Coord
+ \return Screen coord
+ \warning Thera are rounding errors
+ */
+ inline wgtunit_t FindXScreenCoords(const double x) const;
+ /**
+ \brief Finds screen Y coord on the widget
+ \param y Coord
+ \return Screen coord
+ \warning Thera are rounding errors
+ */
+ inline wgtunit_t FindYScreenCoords(const double y) const;
+
+ ///Sets tooltip with legend
+ void MakeLegendTooltip();
+
+ public:
+ /**
+ \brief Widget's constructor
+ \param p Parent
+ \param x_max Maximum X size
+ \param y_max Maximum Y size
+ \param autom Whether athomagically set the maximum Y size
+ \param uname Unit name
+ */
+ ChartDrawer(QWidget *p = 0, wgtsize_t x_max = 2, wgtsize_t y_max = 1, bool autom = true, const QString & uname = "KB/s");
+ ~ChartDrawer();
+
+ /**
+ \brief Gets maximum X
+ \return Maximum X
+ */
+ wgtsize_t GetXMax() const;
+ /**
+ \brief Gets maximum Y
+ \return Maximum Y
+ */
+ wgtsize_t GetYMax() const;
+
+ /**
+ \brief Sets the units name
+ \param rN Name
+
+ \note It'l be drawn on the chart
+ */
+ void SetUnitName(const QString & rN);
+
+ /**
+ \brief Gets unit name
+ \return name
+ */
+ QString GetUnitName() const;
+ /**
+ \brief Doubleclick handler
+ \param evt Mouse event
+ */
+ void mouseDoubleClickEvent ( QMouseEvent * evt );
+
+ /**
+ \brief Gets mode of OY axis maximum drawing
+ \return mode
+ */
+ MaxMode GetMaxMode() const;
+
+
+ public slots:
+ /**
+ \brief Adds value to given dataset
+ \param idx Dataset index
+ \param val Value to add
+ \param update Whether update widget after adding
+ */
+ void AddValue(const size_t idx, const double val, bool update = true);
+ /**
+ \brief Adds dataset
+ \param Cdd Dataset
+ \param max Whether mark maximum of this dataset
+ */
+ void AddValues(ChartDrawerData Cdd, const bool max = true);
+ /**
+ \brief Adds dataset
+ \param Cdd Dataset
+ \param idx Where
+ \param max Whether mark maximum of this dataset
+ */
+ void AddValues(ChartDrawerData Cdd, const size_t idx, const bool max = true);
+ /**
+ \brief Adds empty dataset
+ \param rN Set's data name
+ \param max Whether mark maximum of this dataset
+ */
+ void AddValuesCnt(const QString & rN, const bool max = true);
+ /**
+ \brief Adds empty dataset
+ \param rP Pen that will be used to drawing
+ \param rN Dataset name
+ \param max Whether mark maximum of this dataset
+ */
+ void AddValuesCnt(const QPen & rP, const QString & rN, const bool max = true );
+
+ /**
+ \brief Removes dataset
+ \param idx Dataset index
+ */
+ void RemoveValuesCnt(const size_t idx);
+ /**
+ \brief Zeroes values
+ \param idx Dataset index
+ */
+ void Zero(const size_t idx);
+
+ ///Finds and sets maximum
+ void FindSetMax();
+
+ /**
+ \brief Toggles marking of the maximum Y value on given dataset
+ \param at dataset
+ \param e Toggle?
+ */
+ void EnableMaxDrawAt(const size_t, const bool);
+ /**
+ \brief Toggles automatic max Y scale settin
+ \param a Toggle?
+ */
+ void EnableAutoMax(bool a);
+
+ /**
+ \brief Sets maximum X
+ \param x X size
+ */
+ void SetXMax(const wgtsize_t x);
+ /**
+ \brief Sets maximum Y
+ \param y Y size
+ */
+ void SetYMax(const wgtsize_t x);
+
+ /**
+ \brief Sets mode of max of OY axis
+ \param mm Mode
+ */
+ void SetMaxMode(const MaxMode mm);
+
+
+ signals:
+ /**
+ \brief Emited when widget is doubleclicked
+ \param evt Mouse event
+ */
+ void DoubleClicked(QMouseEvent * evt);
+
+};
+
+}
+
+#endif
+
diff --git a/plugins/stats/ChartDrawerData.cc b/plugins/stats/ChartDrawerData.cc
new file mode 100644
index 0000000..9a49a95
--- /dev/null
+++ b/plugins/stats/ChartDrawerData.cc
@@ -0,0 +1,100 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "ChartDrawerData.h"
+
+namespace kt {
+
+ChartDrawerData::ChartDrawerData(const QString & rN) : pmQp(new QPen("#000", 1, Qt::SolidLine)), pmVals(new val_t(2, 0.0)), mName(rN)
+{
+}
+
+ChartDrawerData::ChartDrawerData(const size_t s, const QString & rN) : pmQp(new QPen("#000", 1, Qt::SolidLine)), pmVals(new val_t(s, 0.0)), mName(rN)
+{
+}
+
+ChartDrawerData::ChartDrawerData(const QPen & rQp, const QString & rN) : pmQp(new QPen(rQp)), pmVals(new val_t(2, 0.0)), mName(rN)
+{
+}
+
+ChartDrawerData::ChartDrawerData(const QPen & rQp, const size_t s, const QString & rN) : pmQp(new QPen(rQp)), pmVals(new val_t(s, 0.0)), mName(rN)
+{
+}
+
+ChartDrawerData::ChartDrawerData(const ChartDrawerData & rS)
+{
+ pmQp = new QPen(*rS.pmQp);
+ pmVals = new val_t(*rS.pmVals);
+ mName = rS.mName;
+}
+
+ChartDrawerData::~ChartDrawerData()
+{
+ delete pmQp;
+ delete pmVals;
+}
+
+const ChartDrawerData::val_t * ChartDrawerData::GetVals() const
+{
+ return pmVals;
+}
+
+const QPen * ChartDrawerData::GetPen() const
+{
+ return pmQp;
+}
+
+void ChartDrawerData::SetPen(const QPen & rQp)
+{
+ delete pmQp;
+ pmQp = new QPen(rQp);
+}
+
+std::pair<double, size_t> ChartDrawerData::Max() const
+{
+
+ double max = 0.0;
+ size_t cpos = 0;
+ size_t maxpos = 0;
+
+ for(val_t::iterator it = pmVals -> begin(); it != pmVals -> end(); it++)
+ {
+ if(max <= *it)
+ {
+ max = *it;
+ maxpos = cpos;
+ }
+
+ cpos++;
+ }
+
+ return std::make_pair(max, maxpos);
+}
+
+QString ChartDrawerData::GetName() const
+{
+ return mName;
+}
+void ChartDrawerData::SetName( const QString & rN )
+{
+ mName = rN;
+}
+
+} // NS end
diff --git a/plugins/stats/ChartDrawerData.h b/plugins/stats/ChartDrawerData.h
new file mode 100644
index 0000000..561e76a
--- /dev/null
+++ b/plugins/stats/ChartDrawerData.h
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef CHARTDRAWERDATA_H_
+#define CHARTDRAWERDATA_H_
+
+#include <qpen.h>
+#include <vector>
+#include <map>
+
+namespace kt {
+
+class ChartDrawer;
+
+/**
+\brief Container for data used by ChartDrawer
+\author Krzysztof Kundzicz <athantor@gmail.com>
+*/
+class ChartDrawerData
+{
+ friend class ChartDrawer;
+
+ public:
+ ///Type for stroring values
+ typedef std::vector<double> val_t;
+
+ private:
+ ///Pen used for drawing
+ QPen * pmQp;
+ ///Values
+ val_t * pmVals;
+ ///Name of set
+ QString mName;
+
+ public:
+ /**
+ \brief Constructor
+ \param rN Name
+ */
+ ChartDrawerData(const QString & rN);
+ /**
+ \brief Copy constructor
+ \param rS Source
+ */
+ ChartDrawerData(const ChartDrawerData &);
+ /**
+ \brief Constructor
+ \param s Size
+ \param rN Name
+ */
+ ChartDrawerData(const size_t s, const QString & rN);
+ /**
+ \brief Constructor
+ \param rQp Pen used for drawing
+ \param rN Name
+ */
+ ChartDrawerData(const QPen & rQp, const QString & rN);
+ /**
+ \brief Constructor
+ \param rQp Pen used for drawing
+ \param s Size
+ \param rN Name
+ */
+ ChartDrawerData(const QPen & rQp, const size_t s, const QString & rN);
+
+ ///Destructor
+ ~ChartDrawerData();
+
+ /**
+ \brief Gets values
+ \return Pointer to values container
+ */
+ const val_t * GetVals() const;
+ /**
+ \brief Gets pen
+ \return Pointer pen
+ */
+ const QPen * GetPen() const;
+ /**
+ \brief Gets name
+ \return Name
+ */
+ QString GetName() const;
+
+ /**
+ \brief Sets pen
+ \param rQp Pen
+ */
+ void SetPen(const QPen & rQp);
+ /**
+ \brief Sets name
+ \param rN Name
+ */
+ void SetName( const QString & rN );
+
+ /**
+ \brief Finds maximum value
+ \return Pair with value and position
+ */
+ std::pair<double, size_t> Max() const;
+
+};
+
+}
+
+#endif
diff --git a/plugins/stats/Makefile.am b/plugins/stats/Makefile.am
new file mode 100644
index 0000000..e04f417
--- /dev/null
+++ b/plugins/stats/Makefile.am
@@ -0,0 +1,18 @@
+INCLUDES = -I$(top_builddir)/libktorrent -I$(top_builddir)/ktorrent/libktorrent \
+ -I$(srcdir)/../../libktorrent $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = ktstatsplugin.la
+
+ktstatsplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KPARTS) $(all_libraries)
+ktstatsplugin_la_SOURCES = ChartDrawerData.cc ChartDrawer.cc statsspdwgt.ui statsconwgt.ui StatsSpd.cc StatsCon.cc sprefwgt.ui statspluginsettings.kcfgc \
+ StatsPluginPrefsPage.cc StatsPluginPrefs.cc statsplugin.cc
+
+ktstatsplugin_la_LIBADD = $(LIB_QT) ../../libktorrent/libktorrent.la
+
+noinst_HEADERS = ChartDrawerData.h ChartDrawer.h StatsSpd.h StatsCon.h StatsPluginPrefsPage.h StatsPluginPrefs.h statsplugin.h
+
+kde_services_DATA = ktstatsplugin.desktop
+kde_kcfg_DATA = ktstatsplugin.kcfg
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/stats/PeerMonitor.cc b/plugins/stats/PeerMonitor.cc
new file mode 100644
index 0000000..a4cf449
--- /dev/null
+++ b/plugins/stats/PeerMonitor.cc
@@ -0,0 +1,187 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "PeerMonitor.h"
+
+namespace kt {
+
+PeerMonitor::PeerMonitor(kt::TorrentInterface * pTi, std::map<bt::SHA1Hash, PeerMonitor *> * pM) : kt::MonitorInterface(), QObject(), pmTorIface(pTi), pmPeerMMgr(pM)
+{
+
+}
+
+PeerMonitor::~PeerMonitor()
+{
+}
+
+void PeerMonitor::peerAdded (kt::PeerInterface *peer)
+{
+ QMutexLocker lock(&mtx);
+
+ mPeers.push_back( peer );
+}
+
+void PeerMonitor::peerRemoved (kt::PeerInterface *peer)
+{
+
+ QMutexLocker lock(&mtx);
+
+ data_t::iterator it = std::find(mPeers.begin(), mPeers.end(), peer);
+
+ if(it != mPeers.end())
+ {
+ mPeers.erase(it);
+ // *it = 0;
+ }
+
+}
+
+void PeerMonitor::downloadStarted (kt::ChunkDownloadInterface *)
+{
+
+}
+
+void PeerMonitor::downloadRemoved (kt::ChunkDownloadInterface *)
+{
+
+}
+
+void PeerMonitor::stopped ()
+{
+ QMutexLocker lock(&mtx);
+
+ std::fill(mPeers.begin(), mPeers.end(), static_cast<PeerInterface*>( 0 ) );
+// mPeers.clear();
+}
+
+void PeerMonitor::destroyed ()
+{
+ if(pmPeerMMgr -> find(pmTorIface -> getInfoHash()) != pmPeerMMgr -> end() )
+ {
+ pmTorIface -> setMonitor(0);
+ pmPeerMMgr -> erase(pmTorIface -> getInfoHash());
+ delete this;
+ }
+
+}
+
+double PeerMonitor::LeechersUpSpeed()
+{
+ QMutexLocker lock(&mtx);
+
+ double spd = 0.0;
+
+ //without it'll segfault/SIGABRT on stop as in meantime the iterator from
+ // mPeers will be invalidated
+
+ for( data_t::const_iterator it = mPeers.begin(); it != mPeers.end(); it++)
+ {
+ if((it != mPeers.end()) && *it && ( (*it) -> getStats().perc_of_file < 100.0) )
+ {
+ spd += (*it) -> getStats().download_rate;
+ }
+ }
+
+ return spd;
+}
+
+double PeerMonitor::LeechersDownSpeed()
+{
+ QMutexLocker lock(&mtx);
+
+ double spd = 0.0;
+
+
+ for( data_t::const_iterator it = mPeers.begin(); it != mPeers.end(); it++)
+ {
+ if((it != mPeers.end()) && *it && ( (*it) -> getStats().perc_of_file < 100.0) )
+ {
+ spd += (*it) -> getStats().upload_rate;
+ }
+ }
+
+ return spd;
+
+}
+
+double PeerMonitor::SeedersUpSpeed()
+{
+ QMutexLocker lock(&mtx);
+
+ double spd = 0.0;
+
+
+ for( data_t::const_iterator it = mPeers.begin(); it != mPeers.end(); it++)
+ {
+ if((it != mPeers.end()) && *it && ( (*it) -> getStats().perc_of_file == 100.0) )
+ {
+ spd += (*it) -> getStats().download_rate;
+ }
+
+ }
+
+ return spd;
+
+}
+
+uint64_t PeerMonitor::GetLeechers()
+{
+ QMutexLocker lock(&mtx);
+
+ uint64_t l = 0;
+
+
+ for( data_t::const_iterator it = mPeers.begin(); it != mPeers.end(); it++)
+ {
+ if((it != mPeers.end()) && *it && ( (*it) -> getStats().perc_of_file != 100.0) )
+ {
+ l++;
+ }
+
+ }
+
+ return l;
+}
+
+uint64_t PeerMonitor::GetSeeders()
+{
+ QMutexLocker lock(&mtx);
+
+ uint64_t s = 0;
+
+
+ for( data_t::const_iterator it = mPeers.begin(); it != mPeers.end(); it++)
+ {
+ if((it != mPeers.end()) && *it && ( (*it) -> getStats().perc_of_file == 100) )
+ {
+ s++;
+ }
+
+ }
+
+ return s;
+}
+
+kt::TorrentInterface * PeerMonitor::GetTorIface() const
+{
+ return pmTorIface;
+}
+
+} //NS end
diff --git a/plugins/stats/PeerMonitor.h b/plugins/stats/PeerMonitor.h
new file mode 100644
index 0000000..d1ef4fc
--- /dev/null
+++ b/plugins/stats/PeerMonitor.h
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef PEERMONITOR_H_
+#define PEERMONITOR_H_
+
+#include <qmutex.h>
+
+#include <interfaces/monitorinterface.h>
+#include <interfaces/peerinterface.h>
+#include <interfaces/torrentinterface.h>
+#include <util/sha1hash.h>
+
+#include <map>
+#include <list>
+#include <algorithm>
+
+namespace kt {
+
+/**
+\brief Monitors peers
+\author Krzysztof Kundzicz <athantor@gmail.com>
+
+Used for peers statistics
+
+\warning Don't use it, as There Can Be Only One™ and the infowidgetplugin relays on it
+*/
+
+class PeerMonitor : public MonitorInterface, public QObject
+{
+ public:
+ ///Type of conteiner of pointers to peers
+ typedef std::list<PeerInterface *> data_t;
+
+ private:
+ /**
+ \brief Mutex
+
+ Used for locking conteiner with pointers to peers
+ */
+ QMutex mtx;
+ ///Container with pointers to peers
+ data_t mPeers;
+ ///Monitored torrent
+ TorrentInterface * pmTorIface;
+ /**
+ \brief Pointer to PeerMonitor „manager”
+
+ \li \c Key: Monitored torrent hash
+ \li \c Value \c type: Pointer to peer monitor
+ */
+ std::map<bt::SHA1Hash, PeerMonitor *> *pmPeerMMgr;
+
+ public:
+ /**
+ \brief Constructor
+ \param pTi Pointer to monitored torrent
+ \param pM Pointer to PeerMonitor „manager”
+ */
+ PeerMonitor(TorrentInterface * pTi, std::map<bt::SHA1Hash, PeerMonitor *> * pM);
+ virtual ~PeerMonitor();
+
+ virtual void peerAdded (PeerInterface *peer);
+ virtual void peerRemoved (PeerInterface *peer);
+ virtual void downloadStarted (ChunkDownloadInterface *cd);
+ virtual void downloadRemoved (ChunkDownloadInterface *cd);
+ virtual void stopped();
+ virtual void destroyed () ;
+
+ /**
+ \brief Gets speed @ which leechers are uploading to us
+ \return Speed
+ */
+ double LeechersUpSpeed() ;
+ /**
+ \brief Gets speed @ which leechers are downloading from us
+ \return Speed
+ */
+ double LeechersDownSpeed();
+ /**
+ \brief Gets speed @ which seeders are uploading to us
+ \return Speed
+ */
+ double SeedersUpSpeed() ;
+ /**
+ \brief Gets leechers count to which we are connected
+ \return Count
+ */
+ uint64_t GetLeechers() ;
+ /**
+ \brief Gets seeders count to which we are connected
+ \return Count
+ */
+ uint64_t GetSeeders() ;
+
+ /**
+ \brief Gets pointer to monitored torrent
+ \return Pointer to monitored torrent
+ */
+ TorrentInterface * GetTorIface() const;
+
+};
+
+}
+
+#endif
diff --git a/plugins/stats/StatsCon.cc b/plugins/stats/StatsCon.cc
new file mode 100644
index 0000000..069ba75
--- /dev/null
+++ b/plugins/stats/StatsCon.cc
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "StatsCon.h"
+
+namespace kt {
+
+StatsCon::StatsCon(QWidget * p) : StatsConWgt(p), pmPeersConCht(new ChartDrawer(PeersConGbw, StatsPluginSettings::connectionsMeasurements())),
+ pmDHTCht(new ChartDrawer(DHTGbw, StatsPluginSettings::dHTMeasurements()))
+{
+ PeersConGbw->setColumnLayout(0, Qt::Vertical );
+ PeersConGbw->layout()->setSpacing( 6 );
+ PeersConGbw->layout()->setMargin( 11 );
+
+ pmPeersConLay = new QVBoxLayout(PeersConGbw -> layout());
+
+ DHTGbw->setColumnLayout(0, Qt::Vertical );
+ DHTGbw->layout()->setSpacing( 6 );
+ DHTGbw->layout()->setMargin( 11 );
+
+ pmDHTLay = new QVBoxLayout(DHTGbw -> layout());
+
+ //-------------
+
+ pmPeersConLay -> addWidget(pmPeersConCht);
+ pmDHTLay -> addWidget(pmDHTCht);
+
+ //-----------
+
+ pmPeersConCht -> SetUnitName("n");
+
+ pmPeersConCht -> AddValuesCnt(QPen("#f00"), i18n("Leechers connected"));
+ pmPeersConCht -> AddValuesCnt(QPen("#900"), i18n("Leechers in swarms"));
+ pmPeersConCht -> AddValuesCnt(QPen("#00f"), i18n("Seeders connected"));
+ pmPeersConCht -> AddValuesCnt(QPen("#009"), i18n("Seeders in swarms"));
+ pmPeersConCht -> AddValuesCnt(QPen("#0a0"), i18n("Average connected leechers per torrent"));
+ pmPeersConCht -> AddValuesCnt(QPen("#060"), i18n("Average connected seeders per torrent"));
+ pmPeersConCht -> AddValuesCnt(QPen("#099"), i18n("Average connected leechers per running torrent"));
+ pmPeersConCht -> AddValuesCnt(QPen("#055"), i18n("Average connected seeders per running torrent"));
+
+
+ pmDHTCht -> SetUnitName("n");
+
+ pmDHTCht -> AddValuesCnt(QPen("#f00"), i18n("Nodes"));
+ pmDHTCht -> AddValuesCnt(QPen("#00f"), i18n("Tasks"));
+}
+
+StatsCon::~StatsCon()
+{
+ delete pmPeersConCht;
+ delete pmDHTCht;
+
+ delete pmPeersConLay;
+ delete pmDHTLay;
+}
+
+void StatsCon::AddPeersConVal(const size_t idx, const double val)
+{
+ pmPeersConCht -> AddValue(idx, val , false );
+}
+
+void StatsCon::AddDHTVal(const size_t idx, const double val)
+{
+ pmDHTCht -> AddValue(idx, val, false);
+}
+
+void StatsCon::UpdateCharts()
+{
+ pmPeersConCht -> update();
+ pmDHTCht -> update();
+}
+
+void StatsCon::ZeroPeersConn(const size_t idx)
+{
+ pmPeersConCht -> Zero(idx);
+}
+
+void StatsCon::ChangeConnMsmtCnt(const size_t cnt)
+{
+ pmPeersConCht -> SetXMax(cnt);
+}
+
+void StatsCon::ChangeDHTMsmtCnt(const size_t cnt)
+{
+ pmDHTCht -> SetXMax(cnt);
+}
+
+void StatsCon::ChangeChartsMaxMode(const ChartDrawer::MaxMode mm)
+{
+ pmPeersConCht -> SetMaxMode(mm);
+ pmDHTCht -> SetMaxMode(mm);
+}
+
+} //NS
+
+#include "StatsCon.moc"
diff --git a/plugins/stats/StatsCon.h b/plugins/stats/StatsCon.h
new file mode 100644
index 0000000..9d60048
--- /dev/null
+++ b/plugins/stats/StatsCon.h
@@ -0,0 +1,94 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef STATSCON_H_
+#define STATSCON_H_
+
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qtabwidget.h>
+#include <qgroupbox.h>
+
+#include "statspluginsettings.h"
+#include "statsconwgt.h"
+#include "ChartDrawer.h"
+
+namespace kt {
+
+class StatsCon : public StatsConWgt
+{
+ Q_OBJECT
+ private:
+ ///Layout of peers connections
+ QVBoxLayout * pmPeersConLay;
+ ///Layout of DHT stats
+ QVBoxLayout * pmDHTLay;
+
+ ///Chart widget of peers connted
+ ChartDrawer * pmPeersConCht;
+ ///Chart widget of DHT
+ ChartDrawer * pmDHTCht;
+
+ public:
+ StatsCon(QWidget * p = 0);
+ virtual ~StatsCon();
+ /**
+ \brief Adds value to peers connections chart
+ \param idx Dataset index
+ \param val Value
+ */
+ void AddPeersConVal(const size_t idx, const double val);
+ /**
+ \brief Adds value to DHT chart
+ \param idx Dataset index
+ \param val Value
+ */
+ void AddDHTVal(const size_t idx, const double val);
+
+ /**
+ \brief Zeroes data on given idx @ peers connections chart
+ \param idx Dataset index
+ */
+ void ZeroPeersConn(const size_t idx);
+
+ /**
+ \brief Changes connections chart's measurments count
+ \param cnt Measurements
+ */
+ void ChangeConnMsmtCnt(const size_t cnt);
+ /**
+ \brief Changes DHT chart's measurments count
+ \param cnt Measurements
+ */
+ void ChangeDHTMsmtCnt(const size_t cnt);
+ /**
+ \brief Changes charts OY axis maximum mode
+ \param mm Mode
+ */
+ void ChangeChartsMaxMode(const ChartDrawer::MaxMode mm);
+ public slots:
+ ///Updates charts
+ void UpdateCharts();
+
+};
+
+} // NS
+
+#endif
diff --git a/plugins/stats/StatsPluginPrefs.cc b/plugins/stats/StatsPluginPrefs.cc
new file mode 100644
index 0000000..07cfa23
--- /dev/null
+++ b/plugins/stats/StatsPluginPrefs.cc
@@ -0,0 +1,88 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "StatsPluginPrefs.h"
+
+namespace kt {
+
+StatsPluginPrefs::StatsPluginPrefs() : PrefPageInterface(i18n("Statistics"), i18n("Statistics options"), KGlobal::iconLoader()->loadIcon("ktimemon",KIcon::NoGroup)), pmUi(0)
+{
+}
+
+StatsPluginPrefs::~StatsPluginPrefs()
+{
+}
+
+bool StatsPluginPrefs::apply ()
+{
+ StatsPluginSettings::setUpdateChartsEveryGuiUpdates(pmUi -> GuiUpdatesSbw -> value());
+ StatsPluginSettings::setGatherDataEveryMs(pmUi -> DataIvalSbw -> value());
+ StatsPluginSettings::setPeersSpeedDataIval(pmUi -> PeersSpdUpdIvalSbw -> value());
+
+ StatsPluginSettings::setPeersSpeed(pmUi -> PeersSpdCbw -> isChecked());
+ StatsPluginSettings::setDrawSeedersInSwarms(pmUi -> ConnSdrInSwaCbw -> isChecked());
+ StatsPluginSettings::setDrawLeechersInSwarms(pmUi -> ConnLchInSwaCbw -> isChecked());
+
+ StatsPluginSettings::setDownloadMeasurements(pmUi -> DownloadMrmtSbw -> value());
+ StatsPluginSettings::setPeersSpeedMeasurements(pmUi -> PeersSpdMrmtSbw -> value());
+ StatsPluginSettings::setUploadMeasurements(pmUi -> UploadMrmtSbw -> value());
+ StatsPluginSettings::setConnectionsMeasurements(pmUi -> ConnsMrmtSbw -> value());
+ StatsPluginSettings::setDHTMeasurements(pmUi -> DHTMrmtSbw -> value());
+ StatsPluginSettings::setMaxSpdMode(pmUi -> MaxSpdModeCbw -> currentItem());
+
+ StatsPluginSettings::writeConfig();
+
+ emit Applied();
+
+ return true;
+}
+
+void StatsPluginPrefs::createWidget (QWidget *parent)
+{
+ pmUi = new StatsPluginPrefsPage(parent);
+}
+
+void StatsPluginPrefs::updateData ()
+{
+ pmUi -> GuiUpdatesSbw -> setValue(StatsPluginSettings::updateChartsEveryGuiUpdates());
+ pmUi -> DataIvalSbw -> setValue(StatsPluginSettings::gatherDataEveryMs());
+ pmUi -> PeersSpdUpdIvalSbw -> setValue(StatsPluginSettings::peersSpeedDataIval());
+
+ pmUi -> PeersSpdCbw -> setChecked(StatsPluginSettings::peersSpeed());
+ pmUi -> ConnSdrInSwaCbw -> setChecked(StatsPluginSettings::drawSeedersInSwarms());
+ pmUi -> ConnLchInSwaCbw -> setChecked(StatsPluginSettings::drawLeechersInSwarms());
+
+ pmUi -> DownloadMrmtSbw -> setValue(StatsPluginSettings::downloadMeasurements());
+ pmUi -> PeersSpdMrmtSbw -> setValue(StatsPluginSettings::peersSpeedMeasurements());
+ pmUi -> UploadMrmtSbw -> setValue(StatsPluginSettings::uploadMeasurements());
+ pmUi -> ConnsMrmtSbw -> setValue(StatsPluginSettings::connectionsMeasurements());
+ pmUi -> DHTMrmtSbw -> setValue(StatsPluginSettings::dHTMeasurements());
+ pmUi -> MaxSpdModeCbw -> setCurrentItem(StatsPluginSettings::maxSpdMode());
+}
+
+void StatsPluginPrefs::deleteWidget ()
+{
+ delete pmUi;
+}
+
+
+} //NS end
+
+#include "StatsPluginPrefs.moc"
diff --git a/plugins/stats/StatsPluginPrefs.h b/plugins/stats/StatsPluginPrefs.h
new file mode 100644
index 0000000..6d563ec
--- /dev/null
+++ b/plugins/stats/StatsPluginPrefs.h
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef STATSPLUGINPREFS_H_
+#define STATSPLUGINPREFS_H_
+
+#include <qspinbox.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+
+#include <interfaces/prefpageinterface.h>
+
+#include "StatsPluginPrefsPage.h"
+#include "statspluginsettings.h"
+
+namespace kt {
+
+/**
+\brief Prefs page
+\author Krzysztof Kundzicz <athantor@gmail.com>
+*/
+class StatsPluginPrefs : public QObject, public PrefPageInterface
+{
+ Q_OBJECT
+
+ private:
+ ///Widget
+ StatsPluginPrefsPage *pmUi;
+ public:
+ ///Constructor
+ StatsPluginPrefs();
+ ///Destructor
+ virtual ~StatsPluginPrefs();
+
+ virtual bool apply ();
+ virtual void createWidget (QWidget *parent);
+ virtual void updateData ();
+ virtual void deleteWidget ();
+ signals:
+ void Applied();
+};
+
+}
+
+#endif
diff --git a/plugins/stats/StatsPluginPrefsPage.cc b/plugins/stats/StatsPluginPrefsPage.cc
new file mode 100644
index 0000000..1278127
--- /dev/null
+++ b/plugins/stats/StatsPluginPrefsPage.cc
@@ -0,0 +1,29 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "StatsPluginPrefsPage.h"
+
+namespace kt {
+
+StatsPluginPrefsPage::StatsPluginPrefsPage(QWidget *p) : sprefwgt(p)
+{
+}
+
+}// NS END
diff --git a/plugins/stats/StatsPluginPrefsPage.h b/plugins/stats/StatsPluginPrefsPage.h
new file mode 100644
index 0000000..59bd9f1
--- /dev/null
+++ b/plugins/stats/StatsPluginPrefsPage.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef STATSPLUGINPREFSPAGE_H_
+#define STATSPLUGINPREFSPAGE_H_
+
+#include "sprefwgt.h"
+
+namespace kt {
+
+/**
+\brief Prefs widget
+\author Krzysztof Kundzicz <athantor@gmail.com>
+*/
+class StatsPluginPrefsPage : public sprefwgt
+{
+ public:
+ /**
+ \brief Constructor
+ \param p Parent
+ */
+ StatsPluginPrefsPage(QWidget * p = 0);
+};
+
+}
+
+#endif
diff --git a/plugins/stats/StatsSpd.cc b/plugins/stats/StatsSpd.cc
new file mode 100644
index 0000000..e4f9d53
--- /dev/null
+++ b/plugins/stats/StatsSpd.cc
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "StatsSpd.h"
+
+namespace kt {
+
+StatsSpd::StatsSpd(QWidget *p) : StatsSpdWgt(p),
+ pmDownCht(new ChartDrawer(DownSpeedGbw, StatsPluginSettings::downloadMeasurements())),
+ pmPeersSpdCht(new ChartDrawer(PeersSpdGbw, StatsPluginSettings::peersSpeedMeasurements())),
+ pmUpCht(new ChartDrawer(UpSpeedGbw, StatsPluginSettings::uploadMeasurements()))
+{
+
+ DownSpeedGbw->setColumnLayout(0, Qt::Vertical );
+ DownSpeedGbw->layout()->setSpacing( 6 );
+ DownSpeedGbw->layout()->setMargin( 11 );
+
+ pmDSpdLay = new QVBoxLayout(DownSpeedGbw -> layout());
+
+ UpSpeedGbw->setColumnLayout(0, Qt::Vertical );
+ UpSpeedGbw->layout()->setSpacing( 6 );
+ UpSpeedGbw->layout()->setMargin( 11 );
+
+ pmUSpdLay = new QVBoxLayout(UpSpeedGbw -> layout());
+
+ PeersSpdGbw->setColumnLayout(0, Qt::Vertical );
+ PeersSpdGbw->layout()->setSpacing( 6 );
+ PeersSpdGbw->layout()->setMargin( 11 );
+
+ pmPeersSpdLay = new QVBoxLayout(PeersSpdGbw -> layout());
+
+ //-----------------
+
+ pmUSpdLay -> addWidget(pmUpCht);
+ pmDSpdLay -> addWidget(pmDownCht);
+ pmPeersSpdLay -> addWidget(pmPeersSpdCht);
+
+ // ----------------
+
+ pmUpCht -> AddValuesCnt(QPen("#f00"), i18n("Current"));
+ pmDownCht -> AddValuesCnt(QPen("#f00"), i18n("Current"));
+
+ pmUpCht -> AddValuesCnt(QPen("#00f"), i18n("Average"));
+ pmDownCht -> AddValuesCnt(QPen("#00f"), i18n("Average"));
+
+ pmUpCht -> AddValuesCnt(i18n("Limit"), 0);
+ pmDownCht -> AddValuesCnt(i18n("Limit"), 0);
+
+// pmUpCht -> AddValuesCnt(QPen("#f0f"), i18n("Current torrent"));
+// pmDownCht -> AddValuesCnt(QPen("#f0f"), i18n("Current torrent"));
+//
+
+
+ pmPeersSpdCht -> AddValuesCnt(QPen("#090"), i18n("Average from leecher"));
+ pmPeersSpdCht -> AddValuesCnt(QPen("#f00"), i18n("Average to leecher"));
+ pmPeersSpdCht -> AddValuesCnt(QPen("#00f"), i18n("Average from seeder"));
+ pmPeersSpdCht -> AddValuesCnt(QPen("magenta"), i18n("From leechers"));
+ pmPeersSpdCht -> AddValuesCnt(QPen("orange"), i18n("From seeders"));
+
+}
+
+StatsSpd::~StatsSpd()
+{
+ delete pmUpCht;
+ delete pmDownCht;
+ delete pmPeersSpdCht;
+
+ delete pmUSpdLay;
+ delete pmDSpdLay;
+ delete pmPeersSpdLay;
+
+}
+
+void StatsSpd::AddUpSpdVal(const size_t idx, const double val)
+{
+ pmUpCht -> AddValue(idx, val, false);
+}
+
+void StatsSpd::AddDownSpdVal(const size_t idx, const double val)
+{
+ pmDownCht -> AddValue(idx, val , false);
+}
+
+void StatsSpd::AddPeersSpdVal(const size_t idx, const double val)
+{
+ pmPeersSpdCht -> AddValue(idx, val , false);
+}
+
+void StatsSpd::UpdateCharts()
+{
+ pmUpCht -> update();
+ pmDownCht -> update();
+ pmPeersSpdCht -> update();
+}
+
+void StatsSpd::ChangeDownMsmtCnt(const size_t cnt)
+{
+ pmDownCht -> SetXMax(cnt);
+}
+
+void StatsSpd::ChangePrsSpdMsmtCnt(const size_t cnt)
+{
+ pmPeersSpdCht -> SetXMax(cnt);
+}
+
+void StatsSpd::ChangeUpMsmtCnt(const size_t cnt)
+{
+ pmUpCht -> SetXMax(cnt);
+}
+
+void StatsSpd::ChangeChartsMaxMode(const ChartDrawer::MaxMode mm)
+{
+ pmUpCht -> SetMaxMode(mm);
+ pmDownCht -> SetMaxMode(mm);
+ pmPeersSpdCht -> SetMaxMode(mm);
+}
+
+
+} //NS end
+
+#include "StatsSpd.moc"
diff --git a/plugins/stats/StatsSpd.h b/plugins/stats/StatsSpd.h
new file mode 100644
index 0000000..7ba8d43
--- /dev/null
+++ b/plugins/stats/StatsSpd.h
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef STATSSPD_H_
+#define STATSSPD_H_
+
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qtabwidget.h>
+#include <qgroupbox.h>
+
+#include "statspluginsettings.h"
+#include "statsspdwgt.h"
+#include "ChartDrawer.h"
+
+namespace kt {
+
+/**
+\brief Main widget of stats plugin
+\author Krzysztof Kundzicz <athantor@gmail.com>
+*/
+class StatsSpd : public StatsSpdWgt
+{
+ Q_OBJECT
+
+ private:
+ ///Layout of upload speed
+ QVBoxLayout * pmUSpdLay;
+ ///Layout of down speed
+ QVBoxLayout * pmDSpdLay;
+ ///Layout of peers speed
+ QVBoxLayout * pmPeersSpdLay;
+
+ ///Chart widget of download speed
+ ChartDrawer * pmDownCht;
+ ///Chart widget of peers speed
+ ChartDrawer * pmPeersSpdCht;
+ ///Chart widget of upload speed
+ ChartDrawer * pmUpCht;
+
+ public:
+ /**
+ \brief Constructor
+ \param p Parent
+ */
+ StatsSpd(QWidget *p = 0);
+ ///Destructor
+ virtual ~StatsSpd();
+
+ /**
+ \brief Adds value to upload speed chart
+ \param idx Dataset index
+ \param val Value
+ **/
+ void AddUpSpdVal(const size_t idx, const double val);
+ /**
+ \brief Adds value to download speed chart
+ \param idx Dataset index
+ \param val Value
+ **/
+ void AddDownSpdVal(const size_t idx, const double val);
+ /**
+ \brief Adds value to peers speed chart
+ \param idx Dataset index
+ \param val Value
+ **/
+ void AddPeersSpdVal(const size_t idx, const double val);
+ /**
+ \brief Changes download chart's measurments count
+ \param cnt Measurements
+ */
+ void ChangeDownMsmtCnt(const size_t cnt);
+ /**
+ \brief Changes peers speed chart's measurments count
+ \param cnt Measurements
+ */
+ void ChangePrsSpdMsmtCnt(const size_t cnt);
+ /**
+ \brief Changes upload chart's measurments count
+ \param cnt Measurements
+ */
+ void ChangeUpMsmtCnt(const size_t cnt);
+ /**
+ \brief Changes charts OY axis maximum mode
+ \param mm Mode
+ */
+ void ChangeChartsMaxMode(const ChartDrawer::MaxMode mm);
+
+ public slots:
+ ///Updates charts
+ void UpdateCharts();
+};
+
+}
+
+#endif
diff --git a/plugins/stats/ktstatsplugin.desktop b/plugins/stats/ktstatsplugin.desktop
new file mode 100644
index 0000000..79e620f
--- /dev/null
+++ b/plugins/stats/ktstatsplugin.desktop
@@ -0,0 +1,24 @@
+[Desktop Entry]
+Type=Service
+Name=StatsPlugin
+Name[bg]=Приставка за статистика
+Name[de]=Statistik-Modul
+Name[es]=Complemento de estadísticas
+Name[et]=Statistikaplugin
+Name[it]=Plugin statistiche
+Name[nds]=Statistik-Moduul
+Name[nl]=Statistiekenplugin
+Name[pl]=Wtyczka statystyk
+Name[pt]='Plugin' de Estatísticas
+Name[pt_BR]=Plugin de Estatísticas
+Name[sr]=Прикључак за статистику
+Name[sr@Latn]=Priključak za statistiku
+Name[sv]=Statistikinsticksprogram
+Name[tr]=Arama Eklentisi
+Name[uk]=Втулок статистики
+Name[xx]=xxStatsPluginxx
+Name[zh_CN]=统计插件
+Comment=
+Comment[xx]=xxxx
+ServiceTypes=KTorrent/Plugin
+X-KDE-Library=ktstatsplugin
diff --git a/plugins/stats/ktstatsplugin.kcfg b/plugins/stats/ktstatsplugin.kcfg
new file mode 100644
index 0000000..43c5dd4
--- /dev/null
+++ b/plugins/stats/ktstatsplugin.kcfg
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <kcfgfile name="ktinfowidgetpluginrc"/>
+ <group name="general">
+ <entry name="UpdateChartsEveryGuiUpdates" type="UInt">
+ <label>Update charts every</label>
+ <default>4</default>
+ </entry>
+ <entry name="GatherDataEveryMs" type="UInt">
+ <label>Gather data every</label>
+ <default>1000</default>
+ </entry>
+ <entry name="PeersSpeed" type="Bool">
+ <label>Toggle peers speed charts</label>
+ <default>true</default>
+ </entry>
+ <entry name="PeersSpeedDataIval" type="UInt">
+ <label>Interval between getting data about peers speed</label>
+ <default>4</default>
+ </entry>
+ <entry name="DrawLeechersInSwarms" type="Bool">
+ <label>Toggle drawing of leechers in swarms</label>
+ <default>false</default>
+ </entry>
+ <entry name="DrawSeedersInSwarms" type="Bool">
+ <label>Toggle drawing of seeders in swarms</label>
+ <default>false</default>
+ </entry>
+
+ <entry name="DownloadMeasurements" type="UInt">
+ <label>Download mesurments count</label>
+ <default>256</default>
+ </entry>
+ <entry name="PeersSpeedMeasurements" type="UInt">
+ <label>Peers speed mesurments count</label>
+ <default>256</default>
+ </entry>
+ <entry name="UploadMeasurements" type="UInt">
+ <label>Upload mesurments count</label>
+ <default>256</default>
+ </entry>
+ <entry name="ConnectionsMeasurements" type="UInt">
+ <label>Connections mesurments count</label>
+ <default>512</default>
+ </entry>
+ <entry name="DHTMeasurements" type="UInt">
+ <label>DHT mesurments count</label>
+ <default>512</default>
+ </entry>
+
+ <entry name="MaxSpdMode" type="UInt">
+ <label>OY axis max mode</label>
+ <default>1</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/stats/sprefwgt.ui b/plugins/stats/sprefwgt.ui
new file mode 100644
index 0000000..7b6674a
--- /dev/null
+++ b/plugins/stats/sprefwgt.ui
@@ -0,0 +1,517 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>sprefwgt</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>sprefwgt</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Update</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Update charts every</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>GuiUpdatesSbw</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>GuiUpdatesSbw</cstring>
+ </property>
+ <property name="maxValue">
+ <number>429496729</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>4</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>GUI updates</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>GuiUpdatesSbw</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Gather data every</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>spinBox2</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>DataIvalSbw</cstring>
+ </property>
+ <property name="maxValue">
+ <number>999999999</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="lineStep">
+ <number>250</number>
+ </property>
+ <property name="value">
+ <number>1000</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>miliseconds</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>spinBox2</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="title">
+ <string>Maximum</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_4</cstring>
+ </property>
+ <property name="text">
+ <string>Maximum speed scale mode:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Top</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Exact</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>MaxSpdModeCbw</cstring>
+ </property>
+ <property name="currentItem">
+ <number>1</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>See 'What's this' for more help</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Set maximum value on OY scale as:
+- Top: Globally achieved maximum speed
+- Exact: Maximum achieved speed visible on chart</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Peers speed</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>PeersSpdCbw</cstring>
+ </property>
+ <property name="text">
+ <string>Peers speed:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>update every</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>PeersSpdUpdIvalSbw</cstring>
+ </property>
+ <property name="maxValue">
+ <number>999999999</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>4</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>chart data updates</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Gathering data about many connected peers can be CPU consuming.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox11</cstring>
+ </property>
+ <property name="title">
+ <string>Peers connections</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Large values can obscure charts of connected peers</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>ConnSdrInSwaCbw</cstring>
+ </property>
+ <property name="text">
+ <string>Show seeders in swarms</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>ConnLchInSwaCbw</cstring>
+ </property>
+ <property name="text">
+ <string>Show leechers in swarms</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox8</cstring>
+ </property>
+ <property name="title">
+ <string>Measurements count</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Download</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>DownloadMrmtSbw</cstring>
+ </property>
+ <property name="maxValue">
+ <number>999999999</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>256</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout5_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Peers speed</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>PeersSpdMrmtSbw</cstring>
+ </property>
+ <property name="maxValue">
+ <number>999999999</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>256</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout5_3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3_3</cstring>
+ </property>
+ <property name="text">
+ <string>Upload</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>UploadMrmtSbw</cstring>
+ </property>
+ <property name="maxValue">
+ <number>999999999</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>256</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout5_5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3_5</cstring>
+ </property>
+ <property name="text">
+ <string>Connections</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>ConnsMrmtSbw</cstring>
+ </property>
+ <property name="maxValue">
+ <number>999999999</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>512</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout5_4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3_4</cstring>
+ </property>
+ <property name="text">
+ <string>DHT</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>DHTMrmtSbw</cstring>
+ </property>
+ <property name="maxValue">
+ <number>999999999</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>512</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>PeersSpdCbw</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel2_2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>PeersSpdCbw</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>PeersSpdUpdIvalSbw</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>PeersSpdCbw</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/plugins/stats/statsconwgt.ui b/plugins/stats/statsconwgt.ui
new file mode 100644
index 0000000..07753dc
--- /dev/null
+++ b/plugins/stats/statsconwgt.ui
@@ -0,0 +1,48 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>StatsConWgt</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>StatsConWgt</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>PeersConGbw</cstring>
+ </property>
+ <property name="title">
+ <string>Peers</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>DHTGbw</cstring>
+ </property>
+ <property name="title">
+ <string>DHT</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/plugins/stats/statsplugin.cc b/plugins/stats/statsplugin.cc
new file mode 100644
index 0000000..d3d1b03
--- /dev/null
+++ b/plugins/stats/statsplugin.cc
@@ -0,0 +1,321 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "statsplugin.h"
+
+K_EXPORT_COMPONENT_FACTORY(ktstatsplugin, KGenericFactory<kt::StatsPlugin>("ktstatsplugin"))
+
+namespace kt
+{
+
+StatsPlugin::StatsPlugin(QObject* parent, const char* qt_name, const QStringList& args):
+ Plugin(parent, qt_name, args, "Statistics", i18n("Statistics"),"Krzysztof Kundzicz", "athantor@gmail.com", i18n("Shows transfers statistics"),"ktimemon"), pmUiSpd(0), pmUiCon(0), pmPrefsUi(0), pmUpdTmr(0)
+{
+ mUpAvg = std::make_pair(0.0, 0.0);
+ mDownAvg = std::make_pair(0.0, 0.0);
+ mLeechAvg = std::make_pair(0, 0);
+ mRunningLeechAvg = std::make_pair(0, 0);
+ mSeedAvg = std::make_pair(0, 0);
+ mRunningSeedAvg = std::make_pair(0, 0);
+}
+
+StatsPlugin::~StatsPlugin()
+{
+}
+
+void StatsPlugin::load()
+{
+
+ mUpdCtr = 1;
+ mPeerSpdUpdCtr = 1;
+
+ pmUiSpd = new StatsSpd(dynamic_cast<QWidget *>(parent()));
+ pmUiCon = new StatsCon(dynamic_cast<QWidget *>(parent()));
+ pmPrefsUi = new StatsPluginPrefs();
+ pmUpdTmr = new QTimer(this);
+
+ connect(pmUpdTmr, SIGNAL(timeout () ), this, SLOT(UpdateData()));
+ connect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(RestartTimer()));
+ connect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(TogglePeersSpdCht()));
+ connect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(ToggleLchInSwmDrawing()));
+ connect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(ToggleSdrInSwmDrawing()));
+ connect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(ChangeMsmtsCounts()));
+ connect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(ChangeMaxMode()));
+
+ TogglePeersSpdCht();
+ ChangeMaxMode();
+
+ pmUpdTmr -> start(StatsPluginSettings::gatherDataEveryMs());
+
+ getGUI() -> addToolWidget(pmUiSpd,"ktimemon" ,i18n("Speed statistics"), GUIInterface::DOCK_BOTTOM);
+ getGUI() -> addToolWidget(pmUiCon,"ktimemon" ,i18n("Connection statistics"), GUIInterface::DOCK_BOTTOM);
+ getGUI() -> addPrefPage (pmPrefsUi);
+
+}
+
+void StatsPlugin::unload()
+{
+ getGUI() -> removeToolWidget(pmUiSpd);
+ getGUI() -> removeToolWidget(pmUiCon);
+ getGUI() -> removePrefPage(pmPrefsUi);
+
+ disconnect(pmUpdTmr, SIGNAL(timeout()), this, SLOT(UpdateData()));
+ disconnect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(RestartTimer()));
+ disconnect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(TogglePeersSpdCht()));
+ disconnect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(ToggleLchInSwmDrawing()));
+ disconnect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(ToggleSdrInSwmDrawing()));
+ disconnect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(ChangeMsmtsCounts()));
+ disconnect(pmPrefsUi, SIGNAL(Applied()), this, SLOT(ChangeMaxMode()));
+
+ delete pmUiSpd;
+ delete pmUiCon;
+ delete pmPrefsUi;
+ delete pmUpdTmr;
+}
+
+bool StatsPlugin::versionCheck(const QString& rVer) const
+{
+ return rVer == KT_VERSION_MACRO;
+}
+
+void StatsPlugin::guiUpdate()
+{
+ if(mUpdCtr >= StatsPluginSettings::updateChartsEveryGuiUpdates())
+ {
+ pmUiSpd -> UpdateCharts();
+ pmUiCon -> UpdateCharts();
+ mUpdCtr = 1;
+
+ } else {
+ mUpdCtr++;
+ }
+}
+
+void StatsPlugin::UpdateData()
+{
+ uint32_t lcon = 0;
+ uint32_t lswa = 0;
+ uint32_t scon = 0;
+ uint32_t sswa = 0;
+ uint32_t rlcon = 0;
+ uint32_t rlswa = 0;
+ uint32_t rscon = 0;
+ uint32_t rsswa = 0;
+
+ uint32_t ld = 0;
+ uint32_t lu = 0;
+ uint32_t sd = 0;
+
+ //---------------------------------------
+
+ mDownAvg.first += getCore() -> getStats() . download_speed;
+ mDownAvg.second++;
+
+ mUpAvg.first += getCore() -> getStats() . upload_speed;
+ mUpAvg.second++;
+
+ pmUiSpd -> AddDownSpdVal(0, getCore() -> getStats() . download_speed / 1024.0);
+ pmUiSpd -> AddUpSpdVal(0, getCore() -> getStats() . upload_speed / 1024.0);
+
+ pmUiSpd -> AddDownSpdVal(1, (mDownAvg.first / mDownAvg.second) / 1024.0 );
+ pmUiSpd -> AddUpSpdVal(1, (mUpAvg.first / mUpAvg.second) / 1024.0 );
+
+ pmUiSpd -> AddDownSpdVal(2, getCore() -> getMaxDownloadSpeed () );
+ pmUiSpd -> AddUpSpdVal(2, getCore() -> getMaxUploadSpeed ());
+
+// if(getGUI()-> getCurrentTorrent())
+// {
+// pmUi -> AddDownSpdVal(3, getGUI()-> getCurrentTorrent() -> getStats() . download_rate / 1024.0);
+// pmUi -> AddUpSpdVal(3, getGUI()-> getCurrentTorrent() -> getStats() . upload_rate / 1024.0);
+// } else {
+// pmUi -> AddDownSpdVal(3, 0.0);
+// pmUi -> AddUpSpdVal(3, 0.0);
+// }
+
+ // ------
+
+ bt::QueueManager::iterator tor = getCore() -> getQueueManager () -> begin();
+
+ while(tor != getCore() -> getQueueManager () -> end())
+ {
+ lcon += (*tor) -> getStats().leechers_connected_to;
+ lswa += (*tor) -> getStats().leechers_total;
+ scon += (*tor) -> getStats().seeders_connected_to;
+ sswa += (*tor) -> getStats().seeders_total;
+
+ mLeechAvg.first += lcon;
+ mLeechAvg.second += lswa;
+ mSeedAvg.first += scon;
+ mSeedAvg.second += sswa;
+
+ if(StatsPluginSettings::peersSpeed() && ( mPeerSpdUpdCtr >= StatsPluginSettings::peersSpeedDataIval() ) )
+ {
+ bt::TorrentControl * tc = dynamic_cast<bt::TorrentControl *>( *tor );
+ const bt::PeerManager * pm = tc->getPeerMgr();
+ if(tc && pm)
+ {
+ for(bt::PeerManager::CItr it = pm -> beginPeerList(); it != pm -> endPeerList (); ++it)
+ {
+ if(it && (*it) )
+ {
+ if(!(*it) -> isSeeder())
+ {
+ ld += (*it) -> getDownloadRate();
+ lu += (*it) -> getUploadRate();
+ } else {
+ sd += (*it) -> getDownloadRate();
+ }
+ }
+ }
+ }
+ }
+
+
+ if( (*tor) -> getStats().started)
+ {
+
+ rlcon += (*tor) -> getStats().leechers_connected_to;
+ rlswa += (*tor) -> getStats().leechers_total;
+ rscon += (*tor) -> getStats().seeders_connected_to;
+ rsswa += (*tor) -> getStats().seeders_total;
+
+ mRunningLeechAvg.first += rlcon;
+ mRunningLeechAvg.second += rlswa;
+ mRunningSeedAvg.first += rscon;
+ mRunningSeedAvg.second += rsswa;
+ }
+
+ tor++;
+ }
+
+ // ------
+
+ if(StatsPluginSettings::peersSpeed() )
+ {
+ if( mPeerSpdUpdCtr >= StatsPluginSettings::peersSpeedDataIval() )
+ {
+ pmUiSpd -> AddPeersSpdVal(0, (ld / (lcon * 1.0)) / 1024.0);
+ pmUiSpd -> AddPeersSpdVal(1, (lu / (lcon * 1.0)) / 1024.0);
+ pmUiSpd -> AddPeersSpdVal(2, (sd / (lswa * 1.0)) / 1024.0);
+ pmUiSpd -> AddPeersSpdVal(3, ld / 1024.0);
+ pmUiSpd -> AddPeersSpdVal(4, sd / 1024.0);
+
+ mPeerSpdUpdCtr = 1;
+ } else {
+ mPeerSpdUpdCtr++;
+ }
+ }
+
+ pmUiCon -> AddPeersConVal(0, lcon);
+ if(StatsPluginSettings::drawLeechersInSwarms())
+ {
+ pmUiCon -> AddPeersConVal(1, lswa);
+ }
+ pmUiCon -> AddPeersConVal(2, scon);
+ if(StatsPluginSettings::drawSeedersInSwarms())
+ {
+ pmUiCon -> AddPeersConVal(3, sswa);
+ }
+
+ double cnt = getCore() -> getQueueManager() -> count() * 1.0;
+ double rcnt = getCore() -> getQueueManager() -> getNumRunning() * 1.0;
+
+ pmUiCon -> AddPeersConVal(4, lcon / cnt );
+ pmUiCon -> AddPeersConVal(5, scon / cnt );
+ pmUiCon -> AddPeersConVal(6, lcon / rcnt);
+ pmUiCon -> AddPeersConVal(7, scon / rcnt );
+
+ // -----
+
+ if( bt::Globals::instance().getDHT().isRunning() )
+ {
+ pmUiCon -> AddDHTVal(0, bt::Globals::instance().getDHT(). getStats().num_peers);
+ pmUiCon -> AddDHTVal(1, bt::Globals::instance().getDHT(). getStats().num_tasks);
+ }
+}
+
+void StatsPlugin::RestartTimer()
+{
+ if( (!pmUpdTmr) || (!pmUpdTmr -> isActive()))
+ {
+ return;
+ }
+
+ pmUpdTmr -> stop();
+ pmUpdTmr -> start(StatsPluginSettings::gatherDataEveryMs());
+}
+
+void StatsPlugin::TogglePeersSpdCht()
+{
+ if(StatsPluginSettings::peersSpeed())
+ {
+ if(pmUiSpd -> PeersSpdGbw -> isHidden())
+ {
+ pmUiSpd -> PeersSpdGbw -> setHidden(false);
+ }
+ } else {
+ if(!pmUiSpd -> PeersSpdGbw -> isHidden())
+ {
+ pmUiSpd -> PeersSpdGbw -> setHidden(true);
+ }
+ }
+}
+
+void StatsPlugin::ToggleLchInSwmDrawing()
+{
+ if(!StatsPluginSettings::drawLeechersInSwarms())
+ {
+ pmUiCon -> ZeroPeersConn(1);
+ }
+}
+
+void StatsPlugin::ToggleSdrInSwmDrawing()
+{
+ if(!StatsPluginSettings::drawSeedersInSwarms())
+ {
+ pmUiCon -> ZeroPeersConn(3);
+ }
+}
+
+void StatsPlugin::ChangeMsmtsCounts()
+{
+ pmUiSpd -> ChangeDownMsmtCnt(StatsPluginSettings::downloadMeasurements());
+ pmUiSpd -> ChangePrsSpdMsmtCnt(StatsPluginSettings::peersSpeedMeasurements());
+ pmUiSpd -> ChangeUpMsmtCnt(StatsPluginSettings::uploadMeasurements());
+ pmUiCon -> ChangeConnMsmtCnt(StatsPluginSettings::connectionsMeasurements());
+ pmUiCon -> ChangeDHTMsmtCnt(StatsPluginSettings::dHTMeasurements());
+}
+
+void StatsPlugin::ChangeMaxMode()
+{
+ if(StatsPluginSettings::maxSpdMode() == 0)
+ {
+ pmUiSpd -> ChangeChartsMaxMode(ChartDrawer::MaxModeTop);
+ pmUiCon -> ChangeChartsMaxMode(ChartDrawer::MaxModeTop);
+
+ } else if (StatsPluginSettings::maxSpdMode() == 1) {
+ pmUiSpd -> ChangeChartsMaxMode(ChartDrawer::MaxModeExact);
+ pmUiCon -> ChangeChartsMaxMode(ChartDrawer::MaxModeExact);
+ }
+}
+
+} // NS end
+
+#include "statsplugin.moc"
diff --git a/plugins/stats/statsplugin.h b/plugins/stats/statsplugin.h
new file mode 100644
index 0000000..14f1fcd
--- /dev/null
+++ b/plugins/stats/statsplugin.h
@@ -0,0 +1,153 @@
+/***************************************************************************
+ * Copyright © 2007 by Krzysztof Kundzicz *
+ * athantor@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef StatsPlugin_H_
+#define StatsPlugin_H_
+
+#include <kgenericfactory.h>
+
+#include <qwidget.h>
+#include <qtimer.h>
+
+#include <interfaces/plugin.h>
+#include <interfaces/guiinterface.h>
+#include <interfaces/coreinterface.h>
+#include <torrent/globals.h>
+#include <kademlia/dhtbase.h>
+#include <torrent/queuemanager.h>
+#include <torrent/torrentcontrol.h>
+#include <torrent/peermanager.h>
+#include <torrent/peer.h>
+
+#include "StatsSpd.h"
+#include "StatsCon.h"
+#include "StatsPluginPrefs.h"
+#include "statspluginsettings.h"
+#include <map> // std::pair
+
+namespace kt {
+
+/**
+\brief Statistics plugin
+\author Krzysztof Kundzicz <athantor@gmail.com>
+\version 200705191548
+*/
+class StatsPlugin : public Plugin
+{
+ Q_OBJECT
+
+ private:
+ ///Speed UI of the plugin
+ StatsSpd * pmUiSpd;
+ ///Connections UI of the plugin
+ StatsCon * pmUiCon;
+ ///UI of the pref page
+ StatsPluginPrefs * pmPrefsUi;
+ /**
+ \brief Average upload speed data
+
+ \li \c first: Total speed
+ \li \c second: Measurements count
+ */
+ std::pair<long double, long double> mUpAvg;
+ /**
+ \brief Average download speed data
+
+ \li \c first: Total speed
+ \li \c second: Measurements count
+ */
+ std::pair<long double, long double> mDownAvg;
+ /**
+ \brief Leechers stats
+
+ \li \c first: connected
+ \li \c second: swarm
+ */
+ std::pair<uint32_t, uint32_t> mLeechAvg;
+ /**
+ \brief Leechers stats on running torrents
+
+ \li \c first: connected
+ \li \c second: swarm
+ */
+ std::pair<uint32_t, uint32_t> mRunningLeechAvg;
+ /**
+ \brief Seeders stats
+
+ \li \c first: connected
+ \li \c second: swarm
+ */
+ std::pair<uint32_t, uint32_t> mSeedAvg;
+ /**
+ \brief Seeders stats on running torrents
+
+ \li \c first: connected
+ \li \c second: swarm
+ */
+ std::pair<uint32_t, uint32_t> mRunningSeedAvg;
+
+ ///Data update timer
+ QTimer * pmUpdTmr;
+
+ ///Update ctr
+ uint32_t mUpdCtr;
+ uint32_t mPeerSpdUpdCtr;
+
+ private slots:
+ ///Updates stat data
+ void UpdateData();
+ /**
+ \brief Restarts timer
+
+ Restarts timer when the interval of data gathering has been changed
+ */
+ void RestartTimer();
+ ///Toggles peers speed chart
+ void TogglePeersSpdCht();
+ ///Toggles drawing of total leechers in swarms
+ void ToggleLchInSwmDrawing();
+ ///Toggles drawing of total seeders in swarms
+ void ToggleSdrInSwmDrawing();
+ ///Changes measurements counts
+ void ChangeMsmtsCounts();
+ ///Changes OY max mode
+ void ChangeMaxMode();
+
+ public:
+ /**
+ \brief Constructor
+ \param parent Parent
+ \param qt_name
+ \param args
+ */
+ StatsPlugin(QObject* parent, const char* qt_name, const QStringList& args);
+ ///Destructor
+ virtual ~StatsPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual bool versionCheck(const QString&) const;
+ virtual void guiUpdate();
+};
+
+}
+
+#endif
+
diff --git a/plugins/stats/statspluginsettings.kcfgc b/plugins/stats/statspluginsettings.kcfgc
new file mode 100644
index 0000000..d809d30
--- /dev/null
+++ b/plugins/stats/statspluginsettings.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=ktstatsplugin.kcfg
+ClassName=StatsPluginSettings
+Namespace=kt
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables
diff --git a/plugins/stats/statsspdwgt.ui b/plugins/stats/statsspdwgt.ui
new file mode 100644
index 0000000..162bf9c
--- /dev/null
+++ b/plugins/stats/statsspdwgt.ui
@@ -0,0 +1,56 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>StatsSpdWgt</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>StatsSpdWgt</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>DownSpeedGbw</cstring>
+ </property>
+ <property name="title">
+ <string>Download</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>PeersSpdGbw</cstring>
+ </property>
+ <property name="title">
+ <string>Peers</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>UpSpeedGbw</cstring>
+ </property>
+ <property name="title">
+ <string>Upload</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/plugins/upnp/Makefile.am b/plugins/upnp/Makefile.am
new file mode 100644
index 0000000..8432f90
--- /dev/null
+++ b/plugins/upnp/Makefile.am
@@ -0,0 +1,38 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+
+libktupnp_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libktupnp.la
+libktupnp_la_SOURCES = soap.cpp upnpdescriptionparser.cpp upnpmcastsocket.cpp\
+ upnprouter.cpp
+
+kde_module_LTLIBRARIES = ktupnpplugin.la
+noinst_HEADERS = upnpplugin.h upnpmcastsocket.h upnprouter.h upnpprefpage.h \
+ upnpprefwidget.h upnpdescriptionparser.h soap.h
+ktupnpplugin_la_SOURCES = upnpplugin.cpp upnpprefpage.cpp upnpwidget.ui \
+ upnpprefwidget.cpp upnppluginsettings.kcfgc
+
+# Libs needed by the plugin
+ktupnpplugin_la_LIBADD = libktupnp.la \
+ $(LIB_KPARTS) ../../libktorrent/libktorrent.la \
+ $(LIB_QT) \
+ $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KFILE)
+
+
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktupnpplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+# rc file containing the GUI for the plugin
+# pluginsdir = $(kde_datadir)/ktsearchplugin
+# plugins_DATA = ktsearchpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+kde_services_DATA = ktupnpplugin.desktop
+
+kde_kcfg_DATA = ktupnpplugin.kcfg
+
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/upnp/ktupnpplugin.desktop b/plugins/upnp/ktupnpplugin.desktop
new file mode 100644
index 0000000..67948d7
--- /dev/null
+++ b/plugins/upnp/ktupnpplugin.desktop
@@ -0,0 +1,26 @@
+[Desktop Entry]
+Name=UPnPPlugin
+Name[bg]=Приставка UPnP
+Name[br]=Lugent UPnP
+Name[de]=UPnP-Modul
+Name[el]=Πρόσθετο UPnP
+Name[es]=Complemento UPnP
+Name[et]=UPnP plugin
+Name[it]=Plugin UPnP
+Name[nb]=UPnP-modul
+Name[nds]=UPnP-Moduul
+Name[nl]=UPnP-plugin
+Name[pl]=Wtyczka UPnP
+Name[pt]='Plugin' UPnP
+Name[pt_BR]=Plugin UPnP
+Name[sk]=UPnP Plugin
+Name[sr]=Прикључак за UPnP
+Name[sr@Latn]=Priključak za UPnP
+Name[sv]=UPnP-insticksprogram
+Name[tr]=UPnP Eklentisi
+Name[xx]=xxUPnPPluginxx
+Name[zh_CN]=UPnP 插件
+Name[zh_TW]=UPnP外掛程式
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktupnpplugin
diff --git a/plugins/upnp/ktupnpplugin.kcfg b/plugins/upnp/ktupnpplugin.kcfg
new file mode 100644
index 0000000..acce783
--- /dev/null
+++ b/plugins/upnp/ktupnpplugin.kcfg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <kcfgfile name="ktupnppluginrc"/>
+ <group name="general">
+ <entry name="defaultDevice" type="String">
+ <label>Default UPnP device to use</label>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/upnp/soap.cpp b/plugins/upnp/soap.cpp
new file mode 100644
index 0000000..b155b55
--- /dev/null
+++ b/plugins/upnp/soap.cpp
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "soap.h"
+
+namespace kt
+{
+
+ QString SOAP::createCommand(const QString & action,const QString & service)
+ {
+ QString comm = QString("<?xml version=\"1.0\"?>\r\n"
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<SOAP-ENV:Body>"
+ "<m:%1 xmlns:m=\"%2\"/>"
+ "</SOAP-ENV:Body></SOAP-ENV:Envelope>"
+ "\r\n").arg(action).arg(service);
+
+ return comm;
+ }
+
+ QString SOAP::createCommand(const QString & action,const QString & service,const QValueList<Arg> & args)
+ {
+ QString comm = QString("<?xml version=\"1.0\"?>\r\n"
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<SOAP-ENV:Body>"
+ "<m:%1 xmlns:m=\"%2\">").arg(action).arg(service);
+
+ for (QValueList<Arg>::const_iterator i = args.begin();i != args.end();i++)
+ {
+ const Arg & a = *i;
+ comm += "<" + a.element + ">" + a.value + "</" + a.element + ">";
+ }
+
+ comm += QString("</m:%1></SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n").arg(action);
+ return comm;
+ }
+}
diff --git a/plugins/upnp/soap.h b/plugins/upnp/soap.h
new file mode 100644
index 0000000..c11e2ed
--- /dev/null
+++ b/plugins/upnp/soap.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTSOAP_H
+#define KTSOAP_H
+
+#include <qvaluelist.h>
+#include <qstring.h>
+
+namespace kt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class SOAP
+ {
+ public:
+
+ /**
+ * Create a simple UPnP SOAP command without parameters.
+ * @param action The name of the action
+ * @param service The name of the service
+ * @return The command
+ */
+ static QString createCommand(const QString & action,const QString & service);
+
+ struct Arg
+ {
+ QString element;
+ QString value;
+ };
+
+ /**
+ * Create a UPnP SOAP command with parameters.
+ * @param action The name of the action
+ * @param service The name of the service
+ * @param args Arguments for command
+ * @return The command
+ */
+ static QString createCommand(const QString & action,const QString & service,const QValueList<Arg> & args);
+ };
+
+}
+
+#endif
diff --git a/plugins/upnp/upnpdescriptionparser.cpp b/plugins/upnp/upnpdescriptionparser.cpp
new file mode 100644
index 0000000..43afbc3
--- /dev/null
+++ b/plugins/upnp/upnpdescriptionparser.cpp
@@ -0,0 +1,220 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qxml.h>
+#include <qvaluestack.h>
+#include <util/fileops.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "upnprouter.h"
+#include "upnpdescriptionparser.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ class XMLContentHandler : public QXmlDefaultHandler
+ {
+ enum Status
+ {
+ TOPLEVEL,ROOT,DEVICE,SERVICE,FIELD,OTHER
+ };
+
+ QString tmp;
+ UPnPRouter* router;
+ UPnPService curr_service;
+ QValueStack<Status> status_stack;
+ public:
+ XMLContentHandler(UPnPRouter* router);
+ virtual ~XMLContentHandler();
+
+
+ bool startDocument();
+ bool endDocument();
+ bool startElement(const QString &, const QString & localName, const QString &,
+ const QXmlAttributes & atts);
+ bool endElement(const QString & , const QString & localName, const QString & );
+ bool characters(const QString & ch);
+
+ bool interestingDeviceField(const QString & name);
+ bool interestingServiceField(const QString & name);
+ };
+
+
+ UPnPDescriptionParser::UPnPDescriptionParser()
+ {}
+
+
+ UPnPDescriptionParser::~UPnPDescriptionParser()
+ {}
+
+ bool UPnPDescriptionParser::parse(const QString & file,UPnPRouter* router)
+ {
+ bool ret = true;
+ {
+ QFile fptr(file);
+ if (!fptr.open(IO_ReadOnly))
+ return false;
+
+ QXmlInputSource input(&fptr);
+ XMLContentHandler chandler(router);
+ QXmlSimpleReader reader;
+
+ reader.setContentHandler(&chandler);
+ ret = reader.parse(&input,false);
+ }
+
+ if (!ret)
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "Error parsing XML" << endl;
+ return false;
+ }
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+
+
+ XMLContentHandler::XMLContentHandler(UPnPRouter* router) : router(router)
+ {}
+
+ XMLContentHandler::~XMLContentHandler()
+ {}
+
+
+ bool XMLContentHandler::startDocument()
+ {
+ status_stack.push(TOPLEVEL);
+ return true;
+ }
+
+ bool XMLContentHandler::endDocument()
+ {
+ status_stack.pop();
+ return true;
+ }
+
+ bool XMLContentHandler::interestingDeviceField(const QString & name)
+ {
+ return name == "friendlyName" || name == "manufacturer" || name == "modelDescription" ||
+ name == "modelName" || name == "modelNumber";
+ }
+
+
+ bool XMLContentHandler::interestingServiceField(const QString & name)
+ {
+ return name == "serviceType" || name == "serviceId" || name == "SCPDURL" ||
+ name == "controlURL" || name == "eventSubURL";
+ }
+
+ bool XMLContentHandler::startElement(const QString &, const QString & localName, const QString &,
+ const QXmlAttributes & )
+ {
+ tmp = "";
+ switch (status_stack.top())
+ {
+ case TOPLEVEL:
+ // from toplevel we can only go to root
+ if (localName == "root")
+ status_stack.push(ROOT);
+ else
+ return false;
+ break;
+ case ROOT:
+ // from the root we can go to device or specVersion
+ // we are not interested in the specVersion
+ if (localName == "device")
+ status_stack.push(DEVICE);
+ else
+ status_stack.push(OTHER);
+ break;
+ case DEVICE:
+ // see if it is a field we are interested in
+ if (interestingDeviceField(localName))
+ status_stack.push(FIELD);
+ else
+ status_stack.push(OTHER);
+ break;
+ case SERVICE:
+ if (interestingServiceField(localName))
+ status_stack.push(FIELD);
+ else
+ status_stack.push(OTHER);
+ break;
+ case OTHER:
+ if (localName == "service")
+ status_stack.push(SERVICE);
+ else if (localName == "device")
+ status_stack.push(DEVICE);
+ else
+ status_stack.push(OTHER);
+ break;
+ case FIELD:
+ break;
+ }
+ return true;
+ }
+
+ bool XMLContentHandler::endElement(const QString & , const QString & localName, const QString & )
+ {
+ switch (status_stack.top())
+ {
+ case FIELD:
+ // we have a field so set it
+ status_stack.pop();
+ if (status_stack.top() == DEVICE)
+ {
+ // if we are in a device
+ router->getDescription().setProperty(localName,tmp);
+ }
+ else if (status_stack.top() == SERVICE)
+ {
+ // set a property of a service
+ curr_service.setProperty(localName,tmp);
+ }
+ break;
+ case SERVICE:
+ // add the service
+ router->addService(curr_service);
+ curr_service.clear();
+ // pop the stack
+ status_stack.pop();
+ break;
+ default:
+ status_stack.pop();
+ break;
+ }
+
+ // reset tmp
+ tmp = "";
+ return true;
+ }
+
+
+ bool XMLContentHandler::characters(const QString & ch)
+ {
+ if (ch.length() > 0)
+ {
+ tmp += ch;
+ }
+ return true;
+ }
+
+}
diff --git a/plugins/upnp/upnpdescriptionparser.h b/plugins/upnp/upnpdescriptionparser.h
new file mode 100644
index 0000000..5d4bf1e
--- /dev/null
+++ b/plugins/upnp/upnpdescriptionparser.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTUPNPDESCRIPTIONPARSER_H
+#define KTUPNPDESCRIPTIONPARSER_H
+
+namespace kt
+{
+ class UPnPRouter;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Parses the xml description of a router.
+ */
+ class UPnPDescriptionParser
+ {
+ public:
+ UPnPDescriptionParser();
+ virtual ~UPnPDescriptionParser();
+
+ /**
+ * Parse the xml description.
+ * @param file File it is located in
+ * @param router The router off the xml description
+ * @return true upon success
+ */
+ bool parse(const QString & file,UPnPRouter* router);
+ };
+
+}
+
+#endif
diff --git a/plugins/upnp/upnpmcastsocket.cpp b/plugins/upnp/upnpmcastsocket.cpp
new file mode 100644
index 0000000..47712ea
--- /dev/null
+++ b/plugins/upnp/upnpmcastsocket.cpp
@@ -0,0 +1,312 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <kurl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <qstringlist.h>
+#include <ksocketdevice.h>
+#include <ksocketaddress.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include "upnpmcastsocket.h"
+
+
+
+using namespace KNetwork;
+using namespace bt;
+
+namespace kt
+{
+
+ UPnPMCastSocket::UPnPMCastSocket(bool verbose) : verbose(verbose)
+ {
+ routers.setAutoDelete(true);
+ QObject::connect(this,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
+ QObject::connect(this,SIGNAL(gotError(int)),this,SLOT(onError(int)));
+ setAddressReuseable(true);
+ setFamily(KNetwork::KResolver::IPv4Family);
+ setBlocking(true);
+ for (Uint32 i = 0;i < 10;i++)
+ {
+ if (!bind(QString::null,QString::number(1900 + i)))
+ Out(SYS_PNP|LOG_IMPORTANT) << "Cannot bind to UDP port 1900" << endl;
+ else
+ break;
+ }
+ setBlocking(false);
+ joinUPnPMCastGroup();
+ }
+
+
+ UPnPMCastSocket::~UPnPMCastSocket()
+ {
+ leaveUPnPMCastGroup();
+ QObject::disconnect(this,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
+ QObject::disconnect(this,SIGNAL(gotError(int)),this,SLOT(onError(int)));
+ }
+
+ void UPnPMCastSocket::discover()
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Trying to find UPnP devices on the local network" << endl;
+
+ // send a HTTP M-SEARCH message to 239.255.255.250:1900
+ const char* data = "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: 239.255.255.250:1900\r\n"
+ "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
+ "MAN:\"ssdp:discover\"\r\n"
+ "MX:3\r\n"
+ "\r\n\0";
+
+ if (verbose)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Sending : " << endl;
+ Out(SYS_PNP|LOG_NOTICE) << data << endl;
+ }
+
+ KDatagramSocket::send(KNetwork::KDatagramPacket(data,strlen(data),KInetSocketAddress("239.255.255.250",1900)));
+ }
+
+ void UPnPMCastSocket::onXmlFileDownloaded(UPnPRouter* r,bool success)
+ {
+ if (!success)
+ {
+ // we couldn't download and parse the XML file so
+ // get rid of it
+ r->deleteLater();
+ }
+ else
+ {
+ // add it to the list and emit the signal
+ if (!routers.contains(r->getServer()))
+ {
+ routers.insert(r->getServer(),r);
+ discovered(r);
+ }
+ else
+ {
+ r->deleteLater();
+ }
+ }
+ }
+
+ void UPnPMCastSocket::onReadyRead()
+ {
+ if (bytesAvailable() == 0)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "0 byte UDP packet " << endl;
+ // KDatagramSocket wrongly handles UDP packets with no payload
+ // so we need to deal with it oursleves
+ int fd = socketDevice()->socket();
+ char tmp;
+ read(fd,&tmp,1);
+ return;
+ }
+
+ KNetwork::KDatagramPacket p = KDatagramSocket::receive();
+ if (p.isNull())
+ return;
+
+ if (verbose)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Received : " << endl;
+ Out(SYS_PNP|LOG_NOTICE) << QString(p.data()) << endl;
+ }
+
+ // try to make a router of it
+ UPnPRouter* r = parseResponse(p.data());
+ if (r)
+ {
+ QObject::connect(r,SIGNAL(xmlFileDownloaded( UPnPRouter*, bool )),
+ this,SLOT(onXmlFileDownloaded( UPnPRouter*, bool )));
+
+ // download it's xml file
+ r->downloadXMLFile();
+ }
+ }
+
+ UPnPRouter* UPnPMCastSocket::parseResponse(const QByteArray & arr)
+ {
+ QStringList lines = QStringList::split("\r\n",QString(arr),false);
+ QString server;
+ KURL location;
+
+ /*
+ Out(SYS_PNP|LOG_DEBUG) << "Received : " << endl;
+ for (Uint32 idx = 0;idx < lines.count(); idx++)
+ Out(SYS_PNP|LOG_DEBUG) << lines[idx] << endl;
+ */
+
+ // first read first line and see if contains a HTTP 200 OK message
+ QString line = lines.first();
+ if (!line.contains("HTTP"))
+ {
+ // it is either a 200 OK or a NOTIFY
+ if (!line.contains("NOTIFY") && !line.contains("200"))
+ return 0;
+ }
+ else if (line.contains("M-SEARCH")) // ignore M-SEARCH
+ return 0;
+
+ // quick check that the response being parsed is valid
+ bool validDevice = false;
+ for (Uint32 idx = 0;idx < lines.count() && !validDevice; idx++)
+ {
+ line = lines[idx];
+ if ((line.contains("ST:") || line.contains("NT:")) && line.contains("InternetGatewayDevice"))
+ {
+ validDevice = true;
+ }
+ }
+ if (!validDevice)
+ {
+ // Out(SYS_PNP|LOG_IMPORTANT) << "Not a valid Internet Gateway Device" << endl;
+ return 0;
+ }
+
+ // read all lines and try to find the server and location fields
+ for (Uint32 i = 1;i < lines.count();i++)
+ {
+ line = lines[i];
+ if (line.startsWith("Location") || line.startsWith("LOCATION") || line.startsWith("location"))
+ {
+ location = line.mid(line.find(':') + 1).stripWhiteSpace();
+ if (!location.isValid())
+ return 0;
+ }
+ else if (line.startsWith("Server") || line.startsWith("server") || line.startsWith("SERVER"))
+ {
+ server = line.mid(line.find(':') + 1).stripWhiteSpace();
+ if (server.length() == 0)
+ return 0;
+
+ }
+ }
+
+ if (routers.contains(server))
+ {
+ return 0;
+ }
+ else
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Detected IGD " << server << endl;
+ // everything OK, make a new UPnPRouter
+ return new UPnPRouter(server,location,verbose);
+ }
+ }
+
+ void UPnPMCastSocket::onError(int)
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "UPnPMCastSocket Error : " << errorString() << endl;
+ }
+
+ void UPnPMCastSocket::saveRouters(const QString & file)
+ {
+ QFile fptr(file);
+ if (!fptr.open(IO_WriteOnly))
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "Cannot open file " << file << " : " << fptr.errorString() << endl;
+ return;
+ }
+
+ // file format is simple : 2 lines per router,
+ // one containing the server, the other the location
+ QTextStream fout(&fptr);
+ bt::PtrMap<QString,UPnPRouter>::iterator i = routers.begin();
+ while (i != routers.end())
+ {
+ UPnPRouter* r = i->second;
+ fout << r->getServer() << endl;
+ fout << r->getLocation().prettyURL() << endl;
+ i++;
+ }
+ }
+
+ void UPnPMCastSocket::loadRouters(const QString & file)
+ {
+ QFile fptr(file);
+ if (!fptr.open(IO_ReadOnly))
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "Cannot open file " << file << " : " << fptr.errorString() << endl;
+ return;
+ }
+
+ // file format is simple : 2 lines per router,
+ // one containing the server, the other the location
+ QTextStream fin(&fptr);
+
+ while (!fin.atEnd())
+ {
+ QString server, location;
+ server = fin.readLine();
+ location = fin.readLine();
+ if (!routers.contains(server))
+ {
+ UPnPRouter* r = new UPnPRouter(server,location);
+ // download it's xml file
+ QObject::connect(r,SIGNAL(xmlFileDownloaded( UPnPRouter*, bool )),this,SLOT(onXmlFileDownloaded( UPnPRouter*, bool )));
+ r->downloadXMLFile();
+ }
+ }
+ }
+
+ void UPnPMCastSocket::joinUPnPMCastGroup()
+ {
+ int fd = socketDevice()->socket();
+ struct ip_mreq mreq;
+
+ memset(&mreq,0,sizeof(struct ip_mreq));
+
+ inet_aton("239.255.255.250",&mreq.imr_multiaddr);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+
+ if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(struct ip_mreq)) < 0)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Failed to join multicast group 239.255.255.250" << endl;
+ }
+ }
+
+ void UPnPMCastSocket::leaveUPnPMCastGroup()
+ {
+ int fd = socketDevice()->socket();
+ struct ip_mreq mreq;
+
+ memset(&mreq,0,sizeof(struct ip_mreq));
+
+ inet_aton("239.255.255.250",&mreq.imr_multiaddr);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+
+ if (setsockopt(fd,IPPROTO_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(struct ip_mreq)) < 0)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Failed to leave multicast group 239.255.255.250" << endl;
+ }
+ }
+}
+
+
+
+#include "upnpmcastsocket.moc"
diff --git a/plugins/upnp/upnpmcastsocket.h b/plugins/upnp/upnpmcastsocket.h
new file mode 100644
index 0000000..493c5b9
--- /dev/null
+++ b/plugins/upnp/upnpmcastsocket.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTUPNPMCASTSOCKET_H
+#define KTUPNPMCASTSOCKET_H
+
+#include <util/ptrmap.h>
+#include <kdatagramsocket.h>
+#include <util/constants.h>
+#include "upnprouter.h"
+
+using bt::Uint32;
+
+namespace kt
+{
+ class UPnPRouter;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Socket used to discover UPnP devices. This class will keep track
+ * of all discovered devices.
+ */
+ class UPnPMCastSocket : public KNetwork::KDatagramSocket
+ {
+ Q_OBJECT
+ public:
+ UPnPMCastSocket(bool verbose = false);
+ virtual ~UPnPMCastSocket();
+
+ /// Get the number of routers discovered
+ Uint32 getNumDevicesDiscovered() const {return routers.count();}
+
+ /// Find a router using it's server name
+ UPnPRouter* findDevice(const QString & name) {return routers.find(name);}
+
+ /// Save all routers to a file (for convenience at startup)
+ void saveRouters(const QString & file);
+
+ /// Load all routers from a file
+ void loadRouters(const QString & file);
+
+ public slots:
+ /**
+ * Try to discover a UPnP device on the network.
+ * A signal will be emitted when a device is found.
+ */
+ void discover();
+
+ private slots:
+ void onReadyRead();
+ void onError(int);
+ void onXmlFileDownloaded(UPnPRouter* r,bool success);
+
+ signals:
+ /**
+ * Emitted when a router or internet gateway device is detected.
+ * @param router The router
+ */
+ void discovered(UPnPRouter* router);
+
+ public:
+ UPnPRouter* parseResponse(const QByteArray & arr);
+
+ private:
+ void joinUPnPMCastGroup();
+ void leaveUPnPMCastGroup();
+
+ private:
+ bt::PtrMap<QString,UPnPRouter> routers;
+ bool verbose;
+ };
+}
+
+#endif
diff --git a/plugins/upnp/upnpplugin.cpp b/plugins/upnp/upnpplugin.cpp
new file mode 100644
index 0000000..dbe58b4
--- /dev/null
+++ b/plugins/upnp/upnpplugin.cpp
@@ -0,0 +1,95 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+#include <kstdaction.h>
+#include <kpopupmenu.h>
+#include <interfaces/guiinterface.h>
+#include <util/fileops.h>
+#include "upnpplugin.h"
+#include "upnpmcastsocket.h"
+#include "upnpprefpage.h"
+
+
+#define NAME "UPnP"
+#define AUTHOR "Joris Guisson"
+#define EMAIL "joris.guisson@gmail.com"
+
+
+
+K_EXPORT_COMPONENT_FACTORY(ktupnpplugin,KGenericFactory<kt::UPnPPlugin>("ktupnpplugin"))
+
+namespace kt
+{
+
+ UPnPPlugin::UPnPPlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name, args,NAME,i18n("UPnP"),AUTHOR,EMAIL,i18n("Uses UPnP to automatically forward ports on your router"),"ktupnp")
+ {
+ sock = 0;
+ pref = 0;
+ }
+
+
+ UPnPPlugin::~UPnPPlugin()
+ {
+ delete sock;
+ delete pref;
+ }
+
+
+ void UPnPPlugin::load()
+ {
+ //KIconLoader* iload = KGlobal::iconLoader();
+ sock = new UPnPMCastSocket();
+ pref = new UPnPPrefPage(sock);
+ this->getGUI()->addPrefPage(pref);
+ // load the routers list
+ QString routers_file = KGlobal::dirs()->saveLocation("data","ktorrent") + "routers";
+ if (bt::Exists(routers_file))
+ sock->loadRouters(routers_file);
+ sock->discover();
+ }
+
+ void UPnPPlugin::unload()
+ {
+ QString routers_file = KGlobal::dirs()->saveLocation("data","ktorrent") + "routers";
+ sock->saveRouters(routers_file);
+ this->getGUI()->removePrefPage(pref);
+ sock->close();
+ delete pref;
+ pref = 0;
+ delete sock;
+ sock = 0;
+ }
+
+ void UPnPPlugin::shutdown(bt::WaitJob* job)
+ {
+ pref->shutdown(job);
+ }
+
+ bool UPnPPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+}
+#include "upnpplugin.moc"
diff --git a/plugins/upnp/upnpplugin.h b/plugins/upnp/upnpplugin.h
new file mode 100644
index 0000000..a6ca78a
--- /dev/null
+++ b/plugins/upnp/upnpplugin.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTSEARCHPLUGIN_H
+#define KTSEARCHPLUGIN_H
+
+#include <interfaces/plugin.h>
+
+namespace kt
+{
+ class UPnPMCastSocket;
+ class UPnPPrefPage;
+
+ /**
+ @author Joris Guisson
+ */
+ class UPnPPlugin : public Plugin
+ {
+ Q_OBJECT
+ public:
+ UPnPPlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~UPnPPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual void shutdown(bt::WaitJob* job);
+ virtual bool versionCheck(const QString& version) const;
+ private:
+ UPnPMCastSocket* sock;
+ UPnPPrefPage* pref;
+ };
+
+}
+
+#endif
diff --git a/plugins/upnp/upnppluginsettings.kcfgc b/plugins/upnp/upnppluginsettings.kcfgc
new file mode 100644
index 0000000..6cab465
--- /dev/null
+++ b/plugins/upnp/upnppluginsettings.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=ktupnpplugin.kcfg
+ClassName=UPnPPluginSettings
+Namespace=kt
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables
diff --git a/plugins/upnp/upnpprefpage.cpp b/plugins/upnp/upnpprefpage.cpp
new file mode 100644
index 0000000..dc50c2f
--- /dev/null
+++ b/plugins/upnp/upnpprefpage.cpp
@@ -0,0 +1,67 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include "upnpprefpage.h"
+#include "upnpprefwidget.h"
+#include "upnprouter.h"
+#include "upnpmcastsocket.h"
+
+namespace kt
+{
+
+ UPnPPrefPage::UPnPPrefPage(UPnPMCastSocket* sock): PrefPageInterface(i18n("UPnP"), i18n("UPnP Devices"),KGlobal::iconLoader()->loadIcon("ktupnp",KIcon::NoGroup)),sock(sock)
+ {
+ widget = 0;
+ }
+
+
+ UPnPPrefPage::~UPnPPrefPage()
+ {}
+
+
+ bool UPnPPrefPage::apply()
+ {
+ return true;
+ }
+
+ void UPnPPrefPage::createWidget(QWidget* parent)
+ {
+ widget = new UPnPPrefWidget(parent);
+ QObject::connect(sock,SIGNAL(discovered(UPnPRouter* )),widget,SLOT(addDevice(UPnPRouter* )));
+ QObject::connect(widget,SIGNAL(rescan()),sock,SLOT(discover()));
+ }
+
+ void UPnPPrefPage::deleteWidget()
+ {
+ delete widget;
+ widget = 0;
+ }
+
+ void UPnPPrefPage::updateData()
+ {
+ }
+
+ void UPnPPrefPage::shutdown(bt::WaitJob* job)
+ {
+ widget->shutdown(job);
+ }
+}
diff --git a/plugins/upnp/upnpprefpage.h b/plugins/upnp/upnpprefpage.h
new file mode 100644
index 0000000..7d5b4f5
--- /dev/null
+++ b/plugins/upnp/upnpprefpage.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTUPNPPREFPAGE_H
+#define KTUPNPPREFPAGE_H
+
+#include <interfaces/prefpageinterface.h>
+
+namespace bt
+{
+ class WaitJob;
+}
+
+namespace kt
+{
+ class UPnPMCastSocket;
+ class UPnPPrefWidget;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Page in the preference dialog for the UPnP plugin.
+ */
+ class UPnPPrefPage : public PrefPageInterface
+ {
+ UPnPMCastSocket* sock;
+ UPnPPrefWidget* widget;
+ public:
+ UPnPPrefPage(UPnPMCastSocket* sock);
+ virtual ~UPnPPrefPage();
+
+ virtual bool apply();
+ virtual void createWidget(QWidget* parent);
+ virtual void deleteWidget();
+ virtual void updateData();
+
+ void shutdown(bt::WaitJob* job);
+ };
+
+}
+
+#endif
diff --git a/plugins/upnp/upnpprefwidget.cpp b/plugins/upnp/upnpprefwidget.cpp
new file mode 100644
index 0000000..43e2aec
--- /dev/null
+++ b/plugins/upnp/upnpprefwidget.cpp
@@ -0,0 +1,253 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <klistview.h>
+#include <kmessagebox.h>
+#include <kpushbutton.h>
+#include <torrent/udptrackersocket.h>
+#include <torrent/globals.h>
+#include <torrent/server.h>
+#include <kademlia/dhtbase.h>
+#include "upnpprefwidget.h"
+#include <util/log.h>
+#include <util/error.h>
+#include <util/waitjob.h>
+#include <util/httprequest.h>
+#include <torrent/globals.h>
+#include "upnppluginsettings.h"
+
+using namespace bt;
+
+namespace kt
+{
+ UPnPPrefWidget::UPnPPrefWidget(QWidget* parent, const char* name, WFlags fl)
+ : UPnPWidget(parent,name,fl)
+ {
+ def_router = 0;
+ connect(m_forward_btn,SIGNAL(clicked()),this,SLOT(onForwardBtnClicked()));
+ connect(m_undo_forward_btn,SIGNAL(clicked()),this,SLOT(onUndoForwardBtnClicked()));
+ connect(m_rescan,SIGNAL(clicked()),this,SLOT(onRescanClicked()));
+ bt::Globals::instance().getPortList().setListener(this);
+ }
+
+ UPnPPrefWidget::~UPnPPrefWidget()
+ {
+ bt::Globals::instance().getPortList().setListener(0);
+ }
+
+ void UPnPPrefWidget::shutdown(bt::WaitJob* job)
+ {
+ if (!def_router)
+ return;
+
+ net::PortList & pl = bt::Globals::instance().getPortList();
+ if (pl.count() == 0)
+ return;
+
+ for (net::PortList::iterator i = pl.begin(); i != pl.end();i++)
+ {
+ net::Port & p = *i;
+ if (p.forward)
+ def_router->undoForward(p,job);
+ }
+ }
+
+
+ void UPnPPrefWidget::addDevice(UPnPRouter* r)
+ {
+ connect(r,SIGNAL(updateGUI()),this,SLOT(updatePortMappings()));
+ KListViewItem* item = new KListViewItem(m_device_list,r->getDescription().friendlyName);
+ item->setMultiLinesEnabled(true);
+ itemmap[item] = r;
+ // if we have discovered the default device or there is none
+ // forward it's ports
+ QString def_dev = UPnPPluginSettings::defaultDevice();
+ if (def_dev == r->getServer() || def_dev.length() == 0)
+ {
+ Out(SYS_PNP|LOG_DEBUG) << "Doing default port mappings ..." << endl;
+ UPnPPluginSettings::setDefaultDevice(r->getServer());
+ UPnPPluginSettings::writeConfig();
+
+ try
+ {
+ net::PortList & pl = bt::Globals::instance().getPortList();
+
+ for (net::PortList::iterator i = pl.begin(); i != pl.end();i++)
+ {
+ net::Port & p = *i;
+ if (p.forward)
+ r->forward(p);
+ }
+
+ def_router = r;
+ }
+ catch (Error & e)
+ {
+ KMessageBox::error(this,e.toString());
+ }
+ }
+ }
+
+ void UPnPPrefWidget::onForwardBtnClicked()
+ {
+ KListViewItem* item = (KListViewItem*)m_device_list->currentItem();;
+ if (!item)
+ return;
+
+ UPnPRouter* r = itemmap[item];
+ if (!r)
+ return;
+
+ try
+ {
+ net::PortList & pl = bt::Globals::instance().getPortList();
+
+ for (net::PortList::iterator i = pl.begin(); i != pl.end();i++)
+ {
+ net::Port & p = *i;
+ if (p.forward)
+ r->forward(p);
+ }
+
+ QString def_dev = UPnPPluginSettings::defaultDevice();
+ if (def_dev != r->getServer())
+ {
+ UPnPPluginSettings::setDefaultDevice(r->getServer());
+ UPnPPluginSettings::writeConfig();
+ def_router = r;
+ }
+
+ }
+ catch (Error & e)
+ {
+ KMessageBox::error(this,e.toString());
+ }
+ }
+
+ void UPnPPrefWidget::onRescanClicked()
+ {
+ // clear the list and emit the signal
+ rescan();
+ }
+
+ void UPnPPrefWidget::onUndoForwardBtnClicked()
+ {
+ KListViewItem* item = (KListViewItem*)m_device_list->currentItem();;
+ if (!item)
+ return;
+
+ UPnPRouter* r = itemmap[item];
+ if (!r)
+ return;
+
+ try
+ {
+ net::PortList & pl = bt::Globals::instance().getPortList();
+
+ for (net::PortList::iterator i = pl.begin(); i != pl.end();i++)
+ {
+ net::Port & p = *i;
+ if (p.forward)
+ r->undoForward(p,false);
+ }
+
+ QString def_dev = UPnPPluginSettings::defaultDevice();
+ if (def_dev == r->getServer())
+ {
+ UPnPPluginSettings::setDefaultDevice(QString::null);
+ UPnPPluginSettings::writeConfig();
+ def_router = 0;
+ }
+ }
+ catch (Error & e)
+ {
+ KMessageBox::error(this,e.toString());
+ }
+ }
+
+
+ void UPnPPrefWidget::updatePortMappings()
+ {
+ // update all port mappings
+ QMap<KListViewItem*,UPnPRouter*>::iterator i = itemmap.begin();
+ while (i != itemmap.end())
+ {
+ UPnPRouter* r = i.data();
+ KListViewItem* item = i.key();
+ QString msg,services;
+ QValueList<UPnPRouter::Forwarding>::iterator j = r->beginPortMappings();
+ while (j != r->endPortMappings())
+ {
+ UPnPRouter::Forwarding & f = *j;
+ if (!f.pending_req)
+ {
+ msg += QString::number(f.port.number) + " (";
+ QString prot = (f.port.proto == net::UDP ? "UDP" : "TCP");
+ msg += prot + ")";
+ if (f.service->servicetype.contains("WANPPPConnection"))
+ services += "PPP";
+ else
+ services += "IP";
+ }
+ j++;
+ if (j != r->endPortMappings())
+ {
+ msg += "\n";
+ services += "\n";
+ }
+ }
+ item->setText(1,msg);
+ item->setText(2,services);
+ i++;
+ }
+ }
+
+
+ void UPnPPrefWidget::portAdded(const net::Port & port)
+ {
+ try
+ {
+ if (def_router && port.forward)
+ def_router->forward(port);
+ }
+ catch (Error & e)
+ {
+ Out(SYS_PNP|LOG_DEBUG) << "Error : " << e.toString() << endl;
+ }
+ }
+
+ void UPnPPrefWidget::portRemoved(const net::Port & port)
+ {
+ try
+ {
+ if (def_router && port.forward)
+ def_router->undoForward(port,false);
+ }
+ catch (Error & e)
+ {
+ Out(SYS_PNP|LOG_DEBUG) << "Error : " << e.toString() << endl;
+ }
+ }
+}
+
+
+
+#include "upnpprefwidget.moc"
+
diff --git a/plugins/upnp/upnpprefwidget.h b/plugins/upnp/upnpprefwidget.h
new file mode 100644
index 0000000..16fa31b
--- /dev/null
+++ b/plugins/upnp/upnpprefwidget.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef UPNPPREFWIDGET_H
+#define UPNPPREFWIDGET_H
+
+#include <qmap.h>
+#include "upnprouter.h"
+#include "upnpwidget.h"
+
+class KListViewItem;
+
+namespace bt
+{
+ class WaitJob;
+}
+
+namespace kt
+{
+
+ /**
+ * Widget for the UPnP pref dialog page.
+ */
+ class UPnPPrefWidget : public UPnPWidget,public net::PortListener
+ {
+ Q_OBJECT
+
+ public:
+ UPnPPrefWidget(QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
+ virtual ~UPnPPrefWidget();
+
+ void shutdown(bt::WaitJob* job);
+
+
+ public slots:
+ /**
+ * Add a device to the list.
+ * @param r The device
+ */
+ void addDevice(UPnPRouter* r);
+
+ signals:
+ /**
+ * Emitted when the user presses the rescan button.
+ */
+ void rescan();
+
+
+ protected slots:
+ void onForwardBtnClicked();
+ void onUndoForwardBtnClicked();
+ void onRescanClicked();
+ void updatePortMappings();
+
+ private:
+ virtual void portAdded(const net::Port & port);
+ virtual void portRemoved(const net::Port & port);
+
+ private:
+ QMap<KListViewItem*,UPnPRouter*> itemmap;
+ UPnPRouter* def_router;
+ };
+}
+
+#endif
+
diff --git a/plugins/upnp/upnprouter.cpp b/plugins/upnp/upnprouter.cpp
new file mode 100644
index 0000000..617abf5
--- /dev/null
+++ b/plugins/upnp/upnprouter.cpp
@@ -0,0 +1,459 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <qstringlist.h>
+#include <kio/netaccess.h>
+#include <kio/job.h>
+#include <torrent/globals.h>
+#include <util/log.h>
+#include <util/array.h>
+#include <util/error.h>
+#include <util/functions.h>
+#include <util/fileops.h>
+#include <util/httprequest.h>
+#include <util/waitjob.h>
+#include "upnprouter.h"
+#include "upnpdescriptionparser.h"
+#include "soap.h"
+
+using namespace bt;
+using namespace net;
+
+namespace kt
+{
+ UPnPService::UPnPService()
+ {
+ }
+
+ UPnPService::UPnPService(const UPnPService & s)
+ {
+ this->servicetype = s.servicetype;
+ this->controlurl = s.controlurl;
+ this->eventsuburl = s.eventsuburl;
+ this->serviceid = s.serviceid;
+ this->scpdurl = s.scpdurl;
+ }
+
+ void UPnPService::setProperty(const QString & name,const QString & value)
+ {
+ if (name == "serviceType")
+ servicetype = value;
+ else if (name == "controlURL")
+ controlurl = value;
+ else if (name == "eventSubURL")
+ eventsuburl = value;
+ else if (name == "SCPDURL")
+ scpdurl = value;
+ else if (name == "serviceId")
+ serviceid = value;
+ }
+
+ void UPnPService::clear()
+ {
+ servicetype = controlurl = eventsuburl = scpdurl = serviceid = "";
+ }
+
+ void UPnPService::debugPrintData()
+ {
+ Out(SYS_PNP|LOG_DEBUG) << " servicetype = " << servicetype << endl;
+ Out(SYS_PNP|LOG_DEBUG) << " controlurl = " << controlurl << endl;
+ Out(SYS_PNP|LOG_DEBUG) << " eventsuburl = " << eventsuburl << endl;
+ Out(SYS_PNP|LOG_DEBUG) << " scpdurl = " << scpdurl << endl;
+ Out(SYS_PNP|LOG_DEBUG) << " serviceid = " << serviceid << endl;
+ }
+
+ UPnPService & UPnPService::operator = (const UPnPService & s)
+ {
+ this->servicetype = s.servicetype;
+ this->controlurl = s.controlurl;
+ this->eventsuburl = s.eventsuburl;
+ this->serviceid = s.serviceid;
+ this->scpdurl = s.scpdurl;
+ return *this;
+ }
+
+ ///////////////////////////////////////
+
+ void UPnPDeviceDescription::setProperty(const QString & name,const QString & value)
+ {
+ if (name == "friendlyName")
+ friendlyName = value;
+ else if (name == "manufacturer")
+ manufacturer = value;
+ else if (name == "modelDescription")
+ modelDescription = value;
+ else if (name == "modelName")
+ modelName = value;
+ else if (name == "modelNumber")
+ modelNumber == value;
+ }
+
+ ///////////////////////////////////////
+
+ UPnPRouter::UPnPRouter(const QString & server,const KURL & location,bool verbose) : server(server),location(location),verbose(verbose)
+ {
+ // make the tmp_file unique, current time * a random number should be enough
+ tmp_file = QString("/tmp/ktorrent_upnp_description-%1.xml").arg(bt::GetCurrentTime() * rand());
+ }
+
+
+ UPnPRouter::~UPnPRouter()
+ {
+ QValueList<HTTPRequest*>::iterator i = active_reqs.begin();
+ while (i != active_reqs.end())
+ {
+ (*i)->deleteLater();
+ i++;
+ }
+ }
+
+ void UPnPRouter::addService(const UPnPService & s)
+ {
+ QValueList<UPnPService>::iterator i = services.begin();
+ while (i != services.end())
+ {
+ UPnPService & os = *i;
+ if (s.servicetype == os.servicetype)
+ return;
+ i++;
+ }
+ services.append(s);
+ }
+
+ void UPnPRouter::downloadFinished(KIO::Job* j)
+ {
+ if (j->error())
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "Failed to download " << location << " : " << j->errorString() << endl;
+ return;
+ }
+
+ QString target = tmp_file;
+ // load in the file (target is always local)
+ UPnPDescriptionParser desc_parse;
+ bool ret = desc_parse.parse(target,this);
+ if (!ret)
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "Error parsing router description !" << endl;
+ QString dest = KGlobal::dirs()->saveLocation("data","ktorrent") + "upnp_failure";
+ KIO::file_copy(target,dest,-1,true,false,false);
+ }
+ else
+ {
+ if (verbose)
+ debugPrintData();
+ }
+ xmlFileDownloaded(this,ret);
+ bt::Delete(target);
+ }
+
+ void UPnPRouter::downloadXMLFile()
+ {
+ // downlaod XML description into a temporary file in /tmp
+ KIO::Job* job = KIO::file_copy(location,tmp_file,-1,true,false,false);
+ connect(job,SIGNAL(result(KIO::Job *)),this,SLOT(downloadFinished( KIO::Job* )));
+ }
+
+ void UPnPRouter::debugPrintData()
+ {
+ Out(SYS_PNP|LOG_DEBUG) << "UPnPRouter : " << endl;
+ Out(SYS_PNP|LOG_DEBUG) << "Friendly name = " << desc.friendlyName << endl;
+ Out(SYS_PNP|LOG_DEBUG) << "Manufacterer = " << desc.manufacturer << endl;
+ Out(SYS_PNP|LOG_DEBUG) << "Model description = " << desc.modelDescription << endl;
+ Out(SYS_PNP|LOG_DEBUG) << "Model name = " << desc.modelName << endl;
+ Out(SYS_PNP|LOG_DEBUG) << "Model number = " << desc.modelNumber << endl;
+ for (QValueList<UPnPService>::iterator i = services.begin();i != services.end();i++)
+ {
+ UPnPService & s = *i;
+ Out() << "Service : " << endl;
+ s.debugPrintData();
+ Out(SYS_PNP|LOG_DEBUG) << "Done" << endl;
+ }
+ Out(SYS_PNP|LOG_DEBUG) << "Done" << endl;
+ }
+
+
+ void UPnPRouter::forward(UPnPService* srv,const net::Port & port)
+ {
+ // add all the arguments for the command
+ QValueList<SOAP::Arg> args;
+ SOAP::Arg a;
+ a.element = "NewRemoteHost";
+ args.append(a);
+
+ // the external port
+ a.element = "NewExternalPort";
+ a.value = QString::number(port.number);
+ args.append(a);
+
+ // the protocol
+ a.element = "NewProtocol";
+ a.value = port.proto == TCP ? "TCP" : "UDP";
+ args.append(a);
+
+ // the local port
+ a.element = "NewInternalPort";
+ a.value = QString::number(port.number);
+ args.append(a);
+
+ // the local IP address
+ a.element = "NewInternalClient";
+ a.value = "$LOCAL_IP";// will be replaced by our local ip in bt::HTTPRequest
+ args.append(a);
+
+ a.element = "NewEnabled";
+ a.value = "1";
+ args.append(a);
+
+ a.element = "NewPortMappingDescription";
+ static Uint32 cnt = 0;
+ a.value = QString("KTorrent UPNP %1").arg(cnt++); // TODO: change this
+ args.append(a);
+
+ a.element = "NewLeaseDuration";
+ a.value = "0";
+ args.append(a);
+
+ QString action = "AddPortMapping";
+ QString comm = SOAP::createCommand(action,srv->servicetype,args);
+
+ Forwarding fw = {port,0,srv};
+ // erase old forwarding if one exists
+ QValueList<Forwarding>::iterator itr = fwds.begin();
+ while (itr != fwds.end())
+ {
+ Forwarding & fwo = *itr;
+ if (fwo.port == port && fwo.service == srv)
+ itr = fwds.erase(itr);
+ else
+ itr++;
+ }
+
+ fw.pending_req = sendSoapQuery(comm,srv->servicetype + "#" + action,srv->controlurl);
+ fwds.append(fw);
+ }
+
+ void UPnPRouter::forward(const net::Port & port)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Forwarding port " << port.number << " (" << (port.proto == UDP ? "UDP" : "TCP") << ")" << endl;
+ // first find the right service
+ QValueList<UPnPService>::iterator i = services.begin();
+ while (i != services.end())
+ {
+ UPnPService & s = *i;
+ if (s.servicetype == "urn:schemas-upnp-org:service:WANIPConnection:1" ||
+ s.servicetype == "urn:schemas-upnp-org:service:WANPPPConnection:1")
+ {
+ forward(&s,port);
+ }
+ i++;
+ }
+
+ }
+
+ void UPnPRouter::undoForward(UPnPService* srv,const net::Port & port,bt::WaitJob* waitjob)
+ {
+ // add all the arguments for the command
+ QValueList<SOAP::Arg> args;
+ SOAP::Arg a;
+ a.element = "NewRemoteHost";
+ args.append(a);
+
+ // the external port
+ a.element = "NewExternalPort";
+ a.value = QString::number(port.number);
+ args.append(a);
+
+ // the protocol
+ a.element = "NewProtocol";
+ a.value = port.proto == TCP ? "TCP" : "UDP";
+ args.append(a);
+
+
+ QString action = "DeletePortMapping";
+ QString comm = SOAP::createCommand(action,srv->servicetype,args);
+ bt::HTTPRequest* r = sendSoapQuery(comm,srv->servicetype + "#" + action,srv->controlurl,waitjob != 0);
+
+ if (waitjob)
+ waitjob->addExitOperation(r);
+
+ updateGUI();
+ }
+
+
+ void UPnPRouter::undoForward(const net::Port & port,bt::WaitJob* waitjob)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Undoing forward of port " << port.number
+ << " (" << (port.proto == UDP ? "UDP" : "TCP") << ")" << endl;
+
+ QValueList<Forwarding>::iterator itr = fwds.begin();
+ while (itr != fwds.end())
+ {
+ Forwarding & wd = *itr;
+ if (wd.port == port)
+ {
+ undoForward(wd.service,wd.port,waitjob);
+ itr = fwds.erase(itr);
+ }
+ else
+ {
+ itr++;
+ }
+ }
+ }
+
+ bt::HTTPRequest* UPnPRouter::sendSoapQuery(const QString & query,const QString & soapact,const QString & controlurl,bool at_exit)
+ {
+ // if port is not set, 0 will be returned
+ // thanks to Diego R. Brogna for spotting this bug
+ if (location.port()==0)
+ location.setPort(80);
+
+ QString http_hdr = QString(
+ "POST %1 HTTP/1.1\r\n"
+ "HOST: %2:%3\r\n"
+ "Content-length: $CONTENT_LENGTH\r\n"
+ "Content-Type: text/xml\r\n"
+ "SOAPAction: \"%4\"\r\n"
+ "\r\n").arg(controlurl).arg(location.host()).arg(location.port()).arg(soapact);
+
+
+ HTTPRequest* r = new HTTPRequest(http_hdr,query,location.host(),location.port(),verbose);
+ connect(r,SIGNAL(replyError(bt::HTTPRequest* ,const QString& )),
+ this,SLOT(onReplyError(bt::HTTPRequest* ,const QString& )));
+ connect(r,SIGNAL(replyOK(bt::HTTPRequest* ,const QString& )),
+ this,SLOT(onReplyOK(bt::HTTPRequest* ,const QString& )));
+ connect(r,SIGNAL(error(bt::HTTPRequest*, bool )),
+ this,SLOT(onError(bt::HTTPRequest*, bool )));
+ r->start();
+ if (!at_exit)
+ active_reqs.append(r);
+ return r;
+ }
+
+ void UPnPRouter::httpRequestDone(bt::HTTPRequest* r,bool erase_fwd)
+ {
+ QValueList<Forwarding>::iterator i = fwds.begin();
+ while (i != fwds.end())
+ {
+ Forwarding & fw = *i;
+ if (fw.pending_req == r)
+ {
+ fw.pending_req = 0;
+ if (erase_fwd)
+ fwds.erase(i);
+ break;
+ }
+ i++;
+ }
+
+ updateGUI();
+ active_reqs.remove(r);
+ r->deleteLater();
+ }
+
+ void UPnPRouter::onReplyOK(bt::HTTPRequest* r,const QString &)
+ {
+ if (verbose)
+ Out(SYS_PNP|LOG_NOTICE) << "UPnPRouter : OK" << endl;
+
+ httpRequestDone(r,false);
+ }
+
+ void UPnPRouter::onReplyError(bt::HTTPRequest* r,const QString &)
+ {
+ if (verbose)
+ Out(SYS_PNP|LOG_IMPORTANT) << "UPnPRouter : Error" << endl;
+
+ httpRequestDone(r,true);
+
+ }
+
+ void UPnPRouter::onError(bt::HTTPRequest* r,bool)
+ {
+ httpRequestDone(r,true);
+ }
+
+#if 0
+ QValueList<UPnPService>::iterator UPnPRouter::findPortForwardingService()
+ {
+ QValueList<UPnPService>::iterator i = services.begin();
+ while (i != services.end())
+ {
+ UPnPService & s = *i;
+ if (s.servicetype == "urn:schemas-upnp-org:service:WANIPConnection:1" ||
+ s.servicetype == "urn:schemas-upnp-org:service:WANPPPConnection:1")
+ return i;
+ i++;
+ }
+ return services.end();
+ }
+
+
+ void UPnPRouter::getExternalIP()
+ {
+ // first find the right service
+ QValueList<UPnPService>::iterator i = findPortForwardingService();
+ if (i == services.end())
+ throw Error(i18n("Cannot find port forwarding service in the device's description!"));
+
+ UPnPService & s = *i;
+ QString action = "GetExternalIPAddress";
+ QString comm = SOAP::createCommand(action,s.servicetype);
+ sendSoapQuery(comm,s.servicetype + "#" + action,s.controlurl);
+ }
+
+ void UPnPRouter::isPortForwarded(const net::Port & port)
+ {
+ // first find the right service
+ QValueList<UPnPService>::iterator i = findPortForwardingService();
+ if (i == services.end())
+ throw Error(i18n("Cannot find port forwarding service in the device's description!"));
+
+ // add all the arguments for the command
+ QValueList<SOAP::Arg> args;
+ SOAP::Arg a;
+ a.element = "NewRemoteHost";
+ args.append(a);
+
+ // the external port
+ a.element = "NewExternalPort";
+ a.value = QString::number(port.number);
+ args.append(a);
+
+ // the protocol
+ a.element = "NewProtocol";
+ a.value = port.proto == TCP ? "TCP" : "UDP";
+ args.append(a);
+
+ UPnPService & s = *i;
+ QString action = "GetSpecificPortMappingEntry";
+ QString comm = SOAP::createCommand(action,s.servicetype,args);
+ sendSoapQuery(comm,s.servicetype + "#" + action,s.controlurl);
+ }
+#endif
+
+
+}
+
+#include "upnprouter.moc"
diff --git a/plugins/upnp/upnprouter.h b/plugins/upnp/upnprouter.h
new file mode 100644
index 0000000..a4d32b4
--- /dev/null
+++ b/plugins/upnp/upnprouter.h
@@ -0,0 +1,223 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTUPNPROUTER_H
+#define KTUPNPROUTER_H
+
+#include <kurl.h>
+#include <qstringlist.h>
+#include <kstreamsocket.h>
+#include <net/portlist.h>
+
+using bt::Uint16;
+
+namespace bt
+{
+ class HTTPRequest;
+ class WaitJob;
+}
+
+namespace KIO
+{
+ class Job;
+}
+
+namespace kt
+{
+ /**
+ * Structure describing a UPnP service found in an xml file.
+ */
+ struct UPnPService
+ {
+ QString serviceid;
+ QString servicetype;
+ QString controlurl;
+ QString eventsuburl;
+ QString scpdurl;
+
+ UPnPService();
+ UPnPService(const UPnPService & s);
+
+ /**
+ * Set a property of the service.
+ * @param name Name of the property (matches to variable names)
+ * @param value Value of the property
+ */
+ void setProperty(const QString & name,const QString & value);
+
+ /**
+ * Set all strings to empty.
+ */
+ void clear();
+
+ /// Print the data of this service
+ void debugPrintData();
+
+ /**
+ * Assignment operator
+ * @param s The service to copy
+ * @return *this
+ */
+ UPnPService & operator = (const UPnPService & s);
+ };
+
+ /**
+ * Struct to hold the description of a device
+ */
+ struct UPnPDeviceDescription
+ {
+ QString friendlyName;
+ QString manufacturer;
+ QString modelDescription;
+ QString modelName;
+ QString modelNumber;
+
+ /**
+ * Set a property of the description
+ * @param name Name of the property (matches to variable names)
+ * @param value Value of the property
+ */
+ void setProperty(const QString & name,const QString & value);
+ };
+
+ /**
+ * @author Joris Guisson
+ *
+ * Class representing a UPnP enabled router. This class is also used to communicate
+ * with the router.
+ */
+ class UPnPRouter : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ struct Forwarding
+ {
+ net::Port port;
+ bt::HTTPRequest* pending_req;
+ UPnPService* service;
+ };
+ private:
+ QString server;
+ QString tmp_file;
+ KURL location;
+ UPnPDeviceDescription desc;
+ QValueList<UPnPService> services;
+ QValueList<Forwarding> fwds;
+ QValueList<bt::HTTPRequest*> active_reqs;
+ public:
+ /**
+ * Construct a router.
+ * @param server The name of the router
+ * @param location The location of it's xml description file
+ * @param verbose Print lots of debug info
+ */
+ UPnPRouter(const QString & server,const KURL & location,bool verbose = false);
+ virtual ~UPnPRouter();
+
+ /// Get the name of the server
+ QString getServer() const {return server;}
+
+ /// Get the location of it's xml description
+ KURL getLocation() const {return location;}
+
+ /// Get the device description
+ UPnPDeviceDescription & getDescription() {return desc;}
+
+ /// Get the device description (const version)
+ const UPnPDeviceDescription & getDescription() const {return desc;}
+
+ /**
+ * Download the XML File of the router.
+ */
+ void downloadXMLFile();
+
+ /**
+ * Add a service to the router.
+ * @param s The service
+ */
+ void addService(const UPnPService & s);
+
+#if 0
+ /**
+ * See if a port is forwarded
+ * @param port The Port
+ */
+ void isPortForwarded(const net::Port & port);
+
+ /**
+ * Get the external IP address.
+ */
+ void getExternalIP();
+#endif
+
+ /**
+ * Forward a local port
+ * @param port The local port to forward
+ */
+ void forward(const net::Port & port);
+
+ /**
+ * Undo forwarding
+ * @param port The port
+ * @param waitjob When this is set the jobs needs to be added to the waitjob,
+ * so we can wait for their completeion at exit
+ */
+ void undoForward(const net::Port & port,bt::WaitJob* waitjob = 0);
+
+ void debugPrintData();
+
+ QValueList<Forwarding>::iterator beginPortMappings() {return fwds.begin();}
+ QValueList<Forwarding>::iterator endPortMappings() {return fwds.end();}
+
+ private slots:
+ void onReplyOK(bt::HTTPRequest* r,const QString &);
+ void onReplyError(bt::HTTPRequest* r,const QString &);
+ void onError(bt::HTTPRequest* r,bool);
+ void downloadFinished(KIO::Job* j);
+
+
+
+ signals:
+ /**
+ * Tell the GUI that it needs to be updated.
+ */
+ void updateGUI();
+
+ /**
+ * Signal which indicates that the XML was downloaded successfully or not.
+ * @param r The router which emitted the signal
+ * @param success Wether or not it succeeded
+ */
+ void xmlFileDownloaded(UPnPRouter* r,bool success);
+
+ private:
+ QValueList<UPnPService>::iterator findPortForwardingService();
+
+ bt::HTTPRequest* sendSoapQuery(const QString & query,const QString & soapact,const QString & controlurl,bool at_exit = false);
+ bool verbose;
+
+ void forward(UPnPService* srv,const net::Port & port);
+ void undoForward(UPnPService* srv,const net::Port & port,bt::WaitJob* waitjob);
+ void httpRequestDone(bt::HTTPRequest* r,bool erase_fwd);
+ };
+
+}
+
+#endif
diff --git a/plugins/upnp/upnpwidget.ui b/plugins/upnp/upnpwidget.ui
new file mode 100644
index 0000000..a8f0f7a
--- /dev/null
+++ b/plugins/upnp/upnpwidget.ui
@@ -0,0 +1,139 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>UPnPWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>UPnPWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>561</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>UPnP</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Detected devices:</string>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Device</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Ports Forwarded</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>WAN Connection</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_device_list</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_forward_btn</cstring>
+ </property>
+ <property name="text">
+ <string>Forw&amp;ard Ports</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_undo_forward_btn</cstring>
+ </property>
+ <property name="text">
+ <string>Undo Port Forwarding</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>70</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_rescan</cstring>
+ </property>
+ <property name="text">
+ <string>Rescan</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/webinterface/Makefile.am b/plugins/webinterface/Makefile.am
new file mode 100644
index 0000000..17a80d9
--- /dev/null
+++ b/plugins/webinterface/Makefile.am
@@ -0,0 +1,34 @@
+INCLUDES = -I$(top_builddir)/libktorrent -I$(top_builddir)/ktorrent/libktorrent \
+ -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+kde_module_LTLIBRARIES = ktwebinterfaceplugin.la
+noinst_HEADERS = webinterfaceplugin.h httpserver.h php_handler.h \
+ php_interface.h webinterfaceprefwidget.h webinterfaceprefpage.h httpclienthandler.h \
+ httpresponseheader.h
+ktwebinterfaceplugin_la_SOURCES = webinterfaceplugin.cpp httpserver.cpp \
+ php_handler.cpp php_interface.cpp webinterfacepref.ui \
+ webinterfacepluginsettings.kcfgc webinterfaceprefwidget.cpp webinterfaceprefpage.cpp \
+ httpclienthandler.cpp httpresponseheader.cpp
+# Libs needed by the plugin
+ktwebinterfaceplugin_la_LIBADD = ../../libktorrent/libktorrent.la \
+ $(LIB_KHTML) $(LIB_KPARTS) $(LIB_QT) \
+ $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KFILE)
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktwebinterfaceplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+# rc file containing the GUI for the plugin
+# pluginsdir = $(kde_datadir)/ktsearchplugin
+# plugins_DATA = ktsearchpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+kde_services_DATA = ktwebinterfaceplugin.desktop
+
+kde_kcfg_DATA = ktwebinterfaceplugin.kcfg
+
+ktdatadir = $(kde_datadir)/ktorrent/www
+
+SUBDIRS = www
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/webinterface/httpclienthandler.cpp b/plugins/webinterface/httpclienthandler.cpp
new file mode 100644
index 0000000..d925466
--- /dev/null
+++ b/plugins/webinterface/httpclienthandler.cpp
@@ -0,0 +1,237 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qsocket.h>
+#include <qhttp.h>
+#include <util/log.h>
+#include <util/mmapfile.h>
+#include "httpserver.h"
+#include "httpclienthandler.h"
+#include "httpresponseheader.h"
+#include "php_handler.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ HttpClientHandler::HttpClientHandler(HttpServer* srv,QSocket* sock) : srv(srv),client(sock),php_response_hdr(200)
+ {
+ state = WAITING_FOR_REQUEST;
+ bytes_read = 0;
+ php = 0;
+ }
+
+
+ HttpClientHandler::~HttpClientHandler()
+ {
+ delete client;
+ delete php;
+ }
+
+ void HttpClientHandler::readyToRead()
+ {
+ if (state == WAITING_FOR_REQUEST)
+ {
+ while (client->canReadLine())
+ {
+ QString line = client->readLine();
+ header_data += line;
+ if (header_data.endsWith("\r\n\r\n"))
+ {
+ // We have got the header, so lets parse it
+ handleRequest();
+ break;
+ }
+ }
+ }
+ else if (state == WAITING_FOR_CONTENT)
+ {
+ Uint32 ba = client->bytesAvailable();
+ if (ba + bytes_read < header.contentLength())
+ {
+ client->readBlock((char*)request_data.data() + bytes_read,ba);
+ bytes_read += ba;
+ }
+ else
+ {
+ Uint32 left = header.contentLength() - bytes_read;
+ client->readBlock((char*)request_data.data() + bytes_read,left);
+ bytes_read += left;
+ srv->handlePost(this,header,request_data);
+
+ header_data = "";
+ request_data.resize(0);
+ state = WAITING_FOR_REQUEST;
+ if (client->bytesAvailable() > 0)
+ readyToRead();
+ }
+ }
+ }
+
+ void HttpClientHandler::handleRequest()
+ {
+ header = QHttpRequestHeader(header_data);
+ // Out(SYS_WEB|LOG_DEBUG) << "Parsing request : " << header.toString() << endl;
+ if (header.method() == "POST")
+ {
+ if (header.hasContentLength())
+ {
+ request_data.resize(header.contentLength());
+ state = WAITING_FOR_CONTENT;
+ bytes_read = 0;
+ if (client->bytesAvailable() > 0)
+ readyToRead();
+ }
+ }
+ else if (header.method() == "GET")
+ {
+ srv->handleGet(this,header);
+ header_data = "";
+ request_data.resize(0);
+ }
+ else
+ {
+ srv->handleUnsupportedMethod(this);
+ }
+ }
+
+ bool HttpClientHandler::sendFile(HttpResponseHeader & hdr,const QString & full_path)
+ {
+ // Out(SYS_WEB|LOG_DEBUG) << "Sending file " << full_path << endl;
+ // first look in cache
+ MMapFile* c = srv->cacheLookup(full_path);
+
+ if (!c)
+ {
+ // not in cache so load it
+ c = new MMapFile();
+ if (!c->open(full_path,MMapFile::READ))
+ {
+ delete c;
+ Out(SYS_WEB|LOG_DEBUG) << "Failed to open file " << full_path << endl;
+ return false;
+ }
+ srv->insertIntoCache(full_path,c);
+ }
+
+ hdr.setValue("Content-Length",QString::number(c->getSize()));
+
+ // Out(SYS_WEB|LOG_DEBUG) << "HTTP header : " << endl;
+ // Out(SYS_WEB|LOG_DEBUG) << hdr.toString() << endl;
+
+ QCString d = hdr.toString().utf8();
+ client->writeBlock(d.data(),d.length());
+
+ Uint32 written = 0;
+ Uint32 total = c->getSize();
+ const char* data = (const char*)c->getDataPointer();
+ while (written < total)
+ {
+ Uint32 w = client->writeBlock(data + written,total - written);
+ written += w;
+ }
+ client->flush();
+ // Out(SYS_WEB|LOG_DEBUG) << "Finished sending " << full_path << " (" << written << " bytes)" << endl;
+ return true;
+ }
+
+#define HTTP_404_ERROR "<html><head><title>404 Not Found</title></head><body>The requested file was not found !</body></html>"
+#define HTTP_500_ERROR "<html><head><title>HTTP/1.1 500 Internal Server Error</title></head><body>HTTP/1.1 Internal Server Error<br>%1</body></html>"
+
+
+ void HttpClientHandler::send404(HttpResponseHeader & hdr,const QString & path)
+ {
+ // Out(SYS_WEB|LOG_DEBUG) << "Sending 404 " << path << endl;
+ QString data = HTTP_404_ERROR;
+ hdr.setValue("Content-Length",QString::number(data.length()));
+
+ QTextStream os(client);
+ os.setEncoding( QTextStream::UnicodeUTF8 );
+ os << hdr.toString();
+ os << data;
+ }
+
+ void HttpClientHandler::send500(HttpResponseHeader & hdr)
+ {
+ // Out(SYS_WEB|LOG_DEBUG) << "Sending 500 " << endl;
+ QString data = QString(HTTP_500_ERROR).arg("An internal server error occured !");
+ hdr.setValue("Content-Length",QString::number(data.length()));
+
+ QTextStream os(client);
+ os.setEncoding( QTextStream::UnicodeUTF8 );
+ os << hdr.toString();
+ os << data;
+ }
+
+ void HttpClientHandler::sendResponse(const HttpResponseHeader & hdr)
+ {
+ // Out(SYS_WEB|LOG_DEBUG) << "Sending response " << hdr.toString() << endl;
+ QTextStream os(client);
+ os.setEncoding( QTextStream::UnicodeUTF8 );
+ os << hdr.toString();
+ }
+
+ void HttpClientHandler::executePHPScript(
+ PhpInterface* php_iface,
+ HttpResponseHeader & hdr,
+ const QString & php_exe,
+ const QString & php_file,
+ const QMap<QString,QString> & args)
+ {
+ // Out(SYS_WEB|LOG_DEBUG) << "Launching PHP script " << php_file << endl;
+ php = new PhpHandler(php_exe,php_iface);
+ if (!php->executeScript(php_file,args))
+ {
+ QString data = QString(HTTP_500_ERROR).arg("Failed to launch PHP executable !");
+ hdr.setResponseCode(500);
+ hdr.setValue("Content-Length",QString::number(data.utf8().length()));
+
+ QTextStream os(client);
+ os.setEncoding( QTextStream::UnicodeUTF8 );
+ os << hdr.toString();
+ os << data;
+ state = WAITING_FOR_REQUEST;
+ }
+ else
+ {
+ php_response_hdr = hdr;
+ connect(php,SIGNAL(finished()),this,SLOT(onPHPFinished()));
+ state = PROCESSING_PHP;
+ }
+ }
+
+ void HttpClientHandler::onPHPFinished()
+ {
+ const QByteArray & output = php->getOutput();
+ php_response_hdr.setValue("Content-Length",QString::number(output.size()));
+
+ QTextStream os(client);
+ os.setEncoding( QTextStream::UnicodeUTF8 );
+ os << php_response_hdr.toString();
+ os.writeRawBytes(output.data(),output.size());
+
+ php->deleteLater();
+ php = 0;
+ state = WAITING_FOR_REQUEST;
+ }
+}
+
+#include "httpclienthandler.moc"
+
diff --git a/plugins/webinterface/httpclienthandler.h b/plugins/webinterface/httpclienthandler.h
new file mode 100644
index 0000000..cacf463
--- /dev/null
+++ b/plugins/webinterface/httpclienthandler.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTHTTPCLIENTHANDLER_H
+#define KTHTTPCLIENTHANDLER_H
+
+
+#include <qhttp.h>
+#include <qobject.h>
+#include <util/constants.h>
+#include "httpresponseheader.h"
+
+class QSocket;
+
+
+namespace kt
+{
+ class HttpServer;
+ class PhpHandler;
+ class PhpInterface;
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class HttpClientHandler : public QObject
+ {
+ Q_OBJECT
+ enum State
+ {
+ WAITING_FOR_REQUEST,
+ WAITING_FOR_CONTENT,
+ PROCESSING_PHP
+ };
+ public:
+ HttpClientHandler(HttpServer* srv,QSocket* sock);
+ virtual ~HttpClientHandler();
+
+ void readyToRead();
+ bool sendFile(HttpResponseHeader & hdr,const QString & full_path);
+ void sendResponse(const HttpResponseHeader & hdr);
+ void send404(HttpResponseHeader & hdr,const QString & path);
+ void send500(HttpResponseHeader & hdr);
+
+ void executePHPScript(PhpInterface* php_iface,
+ HttpResponseHeader & hdr,
+ const QString & php_exe,
+ const QString & php_file,
+ const QMap<QString,QString> & args);
+
+ private:
+ void handleRequest();
+
+ private slots:
+ void onPHPFinished();
+
+ private:
+ HttpServer* srv;
+ QSocket* client;
+ State state;
+ QHttpRequestHeader header;
+ QString header_data;
+ QByteArray request_data;
+ bt::Uint32 bytes_read;
+ PhpHandler* php;
+ HttpResponseHeader php_response_hdr;
+ };
+
+}
+
+#endif
diff --git a/plugins/webinterface/httpresponseheader.cpp b/plugins/webinterface/httpresponseheader.cpp
new file mode 100644
index 0000000..da7556f
--- /dev/null
+++ b/plugins/webinterface/httpresponseheader.cpp
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "httpresponseheader.h"
+
+namespace kt
+{
+ static QString ResponseCodeToString(int r)
+ {
+ switch (r)
+ {
+ case 200: return "OK";
+ case 301: return "Moved Permanently";
+ case 304: return "Not Modified";
+ case 404: return "Not Found";
+ }
+ return QString::null;
+ }
+
+ HttpResponseHeader::HttpResponseHeader(int response_code)
+ : response_code(response_code)
+ {
+ }
+
+ HttpResponseHeader::HttpResponseHeader(const HttpResponseHeader & hdr)
+ {
+ response_code = hdr.response_code;
+ fields = hdr.fields;
+ }
+
+ HttpResponseHeader::~HttpResponseHeader()
+ {
+ }
+
+ void HttpResponseHeader::setResponseCode(int rc)
+ {
+ response_code = rc;
+ }
+
+ void HttpResponseHeader::setValue(const QString & key,const QString & value)
+ {
+ fields[key] = value;
+ }
+
+ QString HttpResponseHeader::toString() const
+ {
+ QString str;
+ str += QString("HTTP/1.1 %1 %2\r\n").arg(response_code).arg(ResponseCodeToString(response_code));
+
+ QMap<QString,QString>::const_iterator itr = fields.begin();
+ while (itr != fields.end())
+ {
+ str += QString("%1: %2\r\n").arg(itr.key()).arg(itr.data());
+ itr++;
+ }
+ str += "\r\n";
+ return str;
+ }
+
+
+
+}
diff --git a/plugins/webinterface/httpresponseheader.h b/plugins/webinterface/httpresponseheader.h
new file mode 100644
index 0000000..9672191
--- /dev/null
+++ b/plugins/webinterface/httpresponseheader.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTHTTPRESPONSEHEADER_H
+#define KTHTTPRESPONSEHEADER_H
+
+
+#include <qmap.h>
+#include <qstring.h>
+
+namespace kt
+{
+
+ /**
+ @author Joris Guisson <joris.guisson@gmail.com>
+ */
+ class HttpResponseHeader
+ {
+ int response_code;
+ QMap<QString,QString> fields;
+ public:
+ HttpResponseHeader(int response_code);
+ HttpResponseHeader(const HttpResponseHeader & hdr);
+ virtual ~HttpResponseHeader();
+
+ void setResponseCode(int response_code);
+ void setValue(const QString & key,const QString & value);
+
+ QString toString() const;
+ };
+
+
+}
+
+#endif
diff --git a/plugins/webinterface/httpserver.cpp b/plugins/webinterface/httpserver.cpp
new file mode 100644
index 0000000..e2c0eeb
--- /dev/null
+++ b/plugins/webinterface/httpserver.cpp
@@ -0,0 +1,553 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qtimer.h>
+#include <qcstring.h>
+#include <qdatetime.h>
+#include <kapplication.h>
+#include <kgenericfactory.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kmdcodec.h>
+#include <ktempfile.h>
+
+#include <qfileinfo.h>
+#include <qsocket.h>
+#include <qstringlist.h>
+
+#include <interfaces/coreinterface.h>
+#include <interfaces/torrentinterface.h>
+
+#include <util/log.h>
+#include <util/fileops.h>
+#include <util/functions.h>
+#include <util/mmapfile.h>
+#include "ktversion.h"
+#include "httpserver.h"
+#include "httpclienthandler.h"
+#include "httpresponseheader.h"
+#include "php_handler.h"
+#include "php_interface.h"
+#include "webinterfacepluginsettings.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+
+
+ HttpServer::HttpServer(CoreInterface *core, int port) : QServerSocket(port, 5),core(core),cache(10,23)
+ {
+ php_i = new PhpInterface(core);
+ clients.setAutoDelete(true);
+
+ QStringList dirList = KGlobal::instance()->dirs()->findDirs("data", "ktorrent/www");
+ rootDir = *(dirList.begin());
+ Out(SYS_WEB|LOG_DEBUG) << "WWW Root Directory "<< rootDir <<endl;
+ session.logged_in = false;
+ cache.setAutoDelete(true);
+ }
+
+ HttpServer::~HttpServer()
+ {
+ delete php_i;
+ }
+
+ void HttpServer::newConnection(int s)
+ {
+ QSocket* socket = new QSocket(this);
+ socket->setSocket(s);
+
+ connect(socket, SIGNAL(readyRead()), this, SLOT(slotSocketReadyToRead()));
+ connect(socket, SIGNAL(delayedCloseFinished()), this, SLOT(slotConnectionClosed()));
+ connect(socket, SIGNAL(connectionClosed()), this, SLOT(slotConnectionClosed()));
+
+ HttpClientHandler* handler = new HttpClientHandler(this,socket);
+ clients.insert(socket,handler);
+ Out(SYS_WEB|LOG_NOTICE) << "connection from "<< socket->peerAddress().toString() << endl;
+ }
+
+
+ void HttpServer::slotSocketReadyToRead()
+ {
+ QSocket* client = (QSocket*)sender();
+ HttpClientHandler* handler = clients.find(client);
+ if (!handler)
+ {
+ client->deleteLater();
+ return;
+ }
+
+ handler->readyToRead();
+ }
+
+ static int DecodeEscapedChar(QString & password,int idx)
+ {
+ QChar a = password[idx + 1].lower();
+ QChar b = password[idx + 2].lower();
+ if (!a.isNumber() && !(a.latin1() >= 'a' && a.latin1() <= 'f'))
+ return idx + 2; // not a valid hex digit
+
+ if (!b.isNumber() && !(b.latin1() >= 'a' && b.latin1() <= 'f'))
+ return idx + 2; // not a valid hex digit
+
+ // calculate high and low nibble
+ Uint8 h = (a.latin1() - (a.isNumber() ? '0' : 'a')) << 4;
+ Uint8 l = (b.latin1() - (b.isNumber() ? '0' : 'a'));
+ char r = (char) h | l; // combine them and cast to a char
+ password.replace(idx,3,r);
+ return idx + 1;
+ }
+
+ bool HttpServer::checkLogin(const QHttpRequestHeader & hdr,const QByteArray & data)
+ {
+ if (hdr.contentType() != "application/x-www-form-urlencoded")
+ return false;
+
+ QString username;
+ QString password;
+ QStringList params = QStringList::split("&",QString(data));
+ for (QStringList::iterator i = params.begin();i != params.end();i++)
+ {
+ QString t = *i;
+ if (t.section("=",0,0) == "username")
+ username = t.section("=",1,1);
+ else if (t.section("=",0,0) == "password")
+ password = t.section("=",1,1);
+
+ // check for passwords with url encoded stuff in them and decode them if necessary
+ int idx = 0;
+ while ((idx = password.find('%',idx)) > 0)
+ {
+ if (idx + 2 < password.length())
+ {
+ idx = DecodeEscapedChar(password,idx);
+ }
+ else
+ break;
+ }
+ }
+
+ if (!username.isNull() && !password.isNull())
+ {
+ KMD5 context(password.utf8());
+
+ if(username == WebInterfacePluginSettings::username() &&
+ context.hexDigest().data() == WebInterfacePluginSettings::password())
+ {
+ session.logged_in = true;
+ session.sessionId=rand();
+ session.last_access=QTime::currentTime();
+ Out(SYS_WEB|LOG_NOTICE) << "Webgui login succesfull !" << endl;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool HttpServer::checkSession(const QHttpRequestHeader & hdr)
+ {
+ // check session in cookie
+ int session_id = 0;
+ if (hdr.hasKey("Cookie"))
+ {
+ QString cookie = hdr.value("Cookie");
+ int idx = cookie.find("KT_SESSID=");
+ if (idx == -1)
+ return false;
+
+ QString number;
+ idx += QString("KT_SESSID=").length();
+ while (idx < cookie.length())
+ {
+ if (cookie[idx] >= '0' && cookie[idx] <= '9')
+ number += cookie[idx];
+ else
+ break;
+
+ idx++;
+ }
+
+ session_id = number.toInt();
+ }
+
+
+ if (session_id == session.sessionId)
+ {
+ // check if the session hasn't expired yet
+ if(session.last_access.secsTo(QTime::currentTime())<WebInterfacePluginSettings::sessionTTL())
+ {
+ session.last_access=QTime::currentTime();
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ return false;
+
+ return true;
+ }
+
+ static QString ExtensionToContentType(const QString & ext)
+ {
+ if (ext == "html")
+ return "text/html";
+ else if (ext == "css")
+ return "text/css";
+ else if (ext == "js")
+ return "text/javascript";
+ else if (ext == "gif" || ext == "png" || ext == "ico")
+ return "image/" + ext;
+ else
+ return QString::null;
+ }
+
+ // HTTP needs non translated dates
+ static QString days[] = {
+ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
+ };
+
+ static QString months[] = {
+ "Jan","Feb","Mar","Apr",
+ "May","Jun","Jul","Aug",
+ "Sep","Oct","Nov","Dec"
+ };
+
+ static QString DateTimeToString(const QDateTime & now,bool cookie)
+ {
+ if (!cookie)
+ return now.toString("%1, dd %2 yyyy hh:mm:ss UTC")
+ .arg(days[now.date().dayOfWeek() - 1])
+ .arg(months[now.date().month() - 1]);
+ else
+ return now.toString("%1, dd-%2-yyyy hh:mm:ss GMT")
+ .arg(days[now.date().dayOfWeek() - 1])
+ .arg(months[now.date().month() - 1]);
+ }
+
+ void HttpServer::setDefaultResponseHeaders(HttpResponseHeader & hdr,const QString & content_type,bool with_session_info)
+ {
+ hdr.setValue("Server","KTorrent/" KT_VERSION_MACRO);
+ hdr.setValue("Date",DateTimeToString(QDateTime::currentDateTime(Qt::UTC),false));
+ hdr.setValue("Content-Type",content_type);
+ hdr.setValue("Connection","keep-alive");
+ if (with_session_info && session.sessionId && session.logged_in)
+ {
+ hdr.setValue("Set-Cookie",QString("KT_SESSID=%1").arg(session.sessionId));
+ }
+ }
+
+ void HttpServer::redirectToLoginPage(HttpClientHandler* hdlr)
+ {
+ HttpResponseHeader rhdr(301);
+ setDefaultResponseHeaders(rhdr,"text/html",false);
+ rhdr.setValue("Location","/login.html");
+ QString path = rootDir + bt::DirSeparator() + WebInterfacePluginSettings::skin() + "/login.html";
+ if (!hdlr->sendFile(rhdr,path))
+ {
+ HttpResponseHeader nhdr(404);
+ setDefaultResponseHeaders(nhdr,"text/html",false);
+ hdlr->send404(nhdr,path);
+ }
+ Out(SYS_WEB|LOG_NOTICE) << "Redirecting to /login.html" << endl;
+ }
+
+ void HttpServer::handleGet(HttpClientHandler* hdlr,const QHttpRequestHeader & hdr,bool do_not_check_session)
+ {
+ QString file = hdr.path();
+ if (file == "/")
+ file = "/login.html";
+
+ //Out(SYS_WEB|LOG_DEBUG) << "GET " << hdr.path() << endl;
+
+ KURL url;
+ url.setEncodedPathAndQuery(file);
+
+ QString path = rootDir + bt::DirSeparator() + WebInterfacePluginSettings::skin() + url.path();
+ // first check if the file exists (if not send 404)
+ if (!bt::Exists(path))
+ {
+ HttpResponseHeader rhdr(404);
+ setDefaultResponseHeaders(rhdr,"text/html",false);
+ hdlr->send404(rhdr,path);
+ return;
+ }
+
+ QFileInfo fi(path);
+ QString ext = fi.extension();
+
+ // if it is the login page send that
+ if (file == "/login.html" || file == "/")
+ {
+ session.logged_in = false;
+ ext = "html";
+ path = rootDir + bt::DirSeparator() + WebInterfacePluginSettings::skin() + "/login.html";
+ }
+ else if (!session.logged_in && (ext == "html" || ext == "php"))
+ {
+ // for any html or php page, a login is necessary
+ redirectToLoginPage(hdlr);
+ return;
+ }
+ else if (session.logged_in && !do_not_check_session && (ext == "html" || ext == "php"))
+ {
+ // if we are logged in and it's a html or php page, check the session id
+ if (!checkSession(hdr))
+ {
+ session.logged_in = false;
+ // redirect to login page
+ redirectToLoginPage(hdlr);
+ return;
+ }
+ }
+
+ if (ext == "html")
+ {
+ HttpResponseHeader rhdr(200);
+ setDefaultResponseHeaders(rhdr,"text/html",true);
+ if (path.endsWith("login.html"))
+ {
+ // clear cookie in case of login page
+ QDateTime dt = QDateTime::currentDateTime().addDays(-1);
+ QString cookie = QString("KT_SESSID=666; expires=%1 +0000").arg(DateTimeToString(dt,true));
+ rhdr.setValue("Set-Cookie",cookie);
+ }
+
+ if (!hdlr->sendFile(rhdr,path))
+ {
+ HttpResponseHeader nhdr(404);
+ setDefaultResponseHeaders(nhdr,"text/html",false);
+ hdlr->send404(nhdr,path);
+ }
+ }
+ else if (ext == "css" || ext == "js" || ext == "png" || ext == "ico" || ext == "gif" || ext == "jpg")
+ {
+ if (hdr.hasKey("If-Modified-Since"))
+ {
+ QDateTime dt = parseDate(hdr.value("If-Modified-Since"));
+ if (dt.isValid() && dt < fi.lastModified())
+ {
+ HttpResponseHeader rhdr(304);
+ setDefaultResponseHeaders(rhdr,"text/html",true);
+ rhdr.setValue("Cache-Control","max-age=0");
+ rhdr.setValue("Last-Modified",DateTimeToString(fi.lastModified(),false));
+ rhdr.setValue("Expires",DateTimeToString(QDateTime::currentDateTime(Qt::UTC).addSecs(3600),false));
+ hdlr->sendResponse(rhdr);
+ return;
+ }
+ }
+
+
+ HttpResponseHeader rhdr(200);
+ setDefaultResponseHeaders(rhdr,ExtensionToContentType(ext),true);
+ rhdr.setValue("Last-Modified",DateTimeToString(fi.lastModified(),false));
+ rhdr.setValue("Expires",DateTimeToString(QDateTime::currentDateTime(Qt::UTC).addSecs(3600),false));
+ rhdr.setValue("Cache-Control","private");
+ if (!hdlr->sendFile(rhdr,path))
+ {
+ HttpResponseHeader nhdr(404);
+ setDefaultResponseHeaders(nhdr,"text/html",false);
+ hdlr->send404(nhdr,path);
+ }
+ }
+ else if (ext == "php")
+ {
+ bool redirect = false;
+ bool shutdown = false;
+ if (url.queryItems().count() > 0 && session.logged_in)
+ redirect = php_i->exec(url,shutdown);
+
+ if (shutdown)
+ {
+ // first send back login page
+ redirectToLoginPage(hdlr);
+ QTimer::singleShot(1000,kapp,SLOT(quit()));
+ }
+ else if (redirect)
+ {
+ HttpResponseHeader rhdr(301);
+ setDefaultResponseHeaders(rhdr,"text/html",true);
+ rhdr.setValue("Location",url.encodedPathAndQuery());
+
+ hdlr->executePHPScript(php_i,rhdr,WebInterfacePluginSettings::phpExecutablePath(),
+ path,url.queryItems());
+ }
+ else
+ {
+ HttpResponseHeader rhdr(200);
+ setDefaultResponseHeaders(rhdr,"text/html",true);
+
+ hdlr->executePHPScript(php_i,rhdr,WebInterfacePluginSettings::phpExecutablePath(),
+ path,url.queryItems());
+ }
+ }
+ else
+ {
+ HttpResponseHeader rhdr(404);
+ setDefaultResponseHeaders(rhdr,"text/html",false);
+ hdlr->send404(rhdr,path);
+ }
+ }
+
+ void HttpServer::handlePost(HttpClientHandler* hdlr,const QHttpRequestHeader & hdr,const QByteArray & data)
+ {
+ // this is either a file or a login
+ if (hdr.value("Content-Type").startsWith("multipart/form-data"))
+ {
+ handleTorrentPost(hdlr,hdr,data);
+ }
+ else if (!checkLogin(hdr,data))
+ {
+ QHttpRequestHeader tmp = hdr;
+ tmp.setRequest("GET","/login.html",1,1);
+ handleGet(hdlr,tmp);
+ }
+ else
+ {
+ handleGet(hdlr,hdr,true);
+ }
+ }
+
+ void HttpServer::handleTorrentPost(HttpClientHandler* hdlr,const QHttpRequestHeader & hdr,const QByteArray & data)
+ {
+ const char* ptr = data.data();
+ Uint32 len = data.size();
+ int pos = QString(data).find("\r\n\r\n");
+
+ if (pos == -1 || pos + 4 >= len || ptr[pos + 4] != 'd')
+ {
+ HttpResponseHeader rhdr(500);
+ setDefaultResponseHeaders(rhdr,"text/html",false);
+ hdlr->send500(rhdr);
+ return;
+ }
+
+ // save torrent to a temporary file
+ KTempFile tmp_file(locateLocal("tmp", "ktwebgui-"), ".torrent");
+ QDataStream* out = tmp_file.dataStream();
+ if (!out)
+ {
+ HttpResponseHeader rhdr(500);
+ setDefaultResponseHeaders(rhdr,"text/html",false);
+ hdlr->send500(rhdr);
+ return;
+ }
+
+ out->writeRawBytes(ptr + (pos + 4),len - (pos + 4));
+ tmp_file.sync();
+ tmp_file.setAutoDelete(true);
+
+ Out(SYS_WEB|LOG_NOTICE) << "Loading file " << tmp_file.name() << endl;
+ core->loadSilently(KURL::fromPathOrURL(tmp_file.name()));
+
+ handleGet(hdlr,hdr);
+ }
+
+ void HttpServer::handleUnsupportedMethod(HttpClientHandler* hdlr)
+ {
+ HttpResponseHeader rhdr(500);
+ setDefaultResponseHeaders(rhdr,"text/html",false);
+ hdlr->send500(rhdr);
+ }
+
+ void HttpServer::slotConnectionClosed()
+ {
+ QSocket* socket= (QSocket*)sender();
+ clients.erase(socket);
+ }
+
+ QDateTime HttpServer::parseDate(const QString & str)
+ {
+ /*
+ Potential date formats :
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ */
+ QStringList sl = QStringList::split(" ",str);
+ if (sl.count() == 6)
+ {
+ // RFC 1123 format
+ QDate d;
+ QString month = sl[2];
+ int m = -1;
+ for (int i = 1;i <= 12 && m < 0;i++)
+ if (QDate::shortMonthName(i) == month)
+ m = i;
+
+ d.setYMD(sl[3].toInt(),m,sl[1].toInt());
+
+ QTime t = QTime::fromString(sl[4],Qt::ISODate);
+ return QDateTime(d,t);
+ }
+ else if (sl.count() == 4)
+ {
+ // RFC 1036
+ QStringList dl = QStringList::split("-",sl[1]);
+ if (dl.count() != 3)
+ return QDateTime();
+
+ QDate d;
+ QString month = dl[1];
+ int m = -1;
+ for (int i = 1;i <= 12 && m < 0;i++)
+ if (QDate::shortMonthName(i) == month)
+ m = i;
+
+ d.setYMD(2000 + dl[2].toInt(),m,dl[0].toInt());
+
+ QTime t = QTime::fromString(sl[2],Qt::ISODate);
+ return QDateTime(d,t);
+ }
+ else if (sl.count() == 5)
+ {
+ // ANSI C
+ QDate d;
+ QString month = sl[1];
+ int m = -1;
+ for (int i = 1;i <= 12 && m < 0;i++)
+ if (QDate::shortMonthName(i) == month)
+ m = i;
+
+ d.setYMD(sl[4].toInt(),m,sl[2].toInt());
+
+ QTime t = QTime::fromString(sl[3],Qt::ISODate);
+ return QDateTime(d,t);
+ }
+ else
+ return QDateTime();
+ }
+
+ bt::MMapFile* HttpServer::cacheLookup(const QString & name)
+ {
+ return cache.find(name);
+ }
+
+ void HttpServer::insertIntoCache(const QString & name,bt::MMapFile* file)
+ {
+ cache.insert(name,file);
+ }
+
+}
+
+#include "httpserver.moc"
diff --git a/plugins/webinterface/httpserver.h b/plugins/webinterface/httpserver.h
new file mode 100644
index 0000000..28be441
--- /dev/null
+++ b/plugins/webinterface/httpserver.h
@@ -0,0 +1,104 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef HTTPSERVER_H
+#define HTTPSERVER_H
+
+#include <qcache.h>
+#include <qhttp.h>
+#include <qdatetime.h>
+#include <qserversocket.h>
+#include <util/ptrmap.h>
+
+class QSocket;
+
+namespace bt
+{
+ class MMapFile;
+}
+
+namespace kt
+{
+ class CoreInterface;
+
+ /**
+ * @author Diego R. Brogna
+ */
+ struct Session
+ {
+ bool logged_in;
+ QTime last_access;
+ int sessionId;
+ };
+
+ struct HeaderField
+ {
+ bool gzip;
+ bool keepAlive;
+ int sessionId;
+ bool ifModifiedSince;
+ };
+
+ class PhpHandler;
+ class PhpInterface;
+ class HttpClientHandler;
+ class HttpResponseHeader;
+
+
+
+ class HttpServer : public QServerSocket
+ {
+ Q_OBJECT
+ public:
+ HttpServer(CoreInterface *core, int port);
+ virtual ~HttpServer();
+
+ void newConnection(int s);
+
+ void handleGet(HttpClientHandler* hdlr,const QHttpRequestHeader & hdr,bool do_not_check_session = false);
+ void handlePost(HttpClientHandler* hdlr,const QHttpRequestHeader & hdr,const QByteArray & data);
+ void handleUnsupportedMethod(HttpClientHandler* hdlr);
+ bt::MMapFile* cacheLookup(const QString & name);
+ void insertIntoCache(const QString & name,bt::MMapFile* file);
+
+ protected slots:
+ void slotSocketReadyToRead();
+ void slotConnectionClosed();
+
+ private:
+ bool checkSession(const QHttpRequestHeader & hdr);
+ bool checkLogin(const QHttpRequestHeader & hdr,const QByteArray & data);
+ void setDefaultResponseHeaders(HttpResponseHeader & hdr,const QString & content_type,bool with_session_info);
+ void handleTorrentPost(HttpClientHandler* hdlr,const QHttpRequestHeader & hdr,const QByteArray & data);
+ QDateTime parseDate(const QString & str);
+ void redirectToLoginPage(HttpClientHandler* hdlr);
+
+ private:
+ QString rootDir;
+ int sessionTTL;
+ PhpInterface *php_i;
+ Session session;
+ bt::PtrMap<QSocket*,HttpClientHandler> clients;
+ CoreInterface *core;
+ QCache<bt::MMapFile> cache;
+ };
+
+
+}
+#endif // HTTPSERVER_H
diff --git a/plugins/webinterface/ktwebinterfaceplugin.desktop b/plugins/webinterface/ktwebinterfaceplugin.desktop
new file mode 100644
index 0000000..f1fc92b
--- /dev/null
+++ b/plugins/webinterface/ktwebinterfaceplugin.desktop
@@ -0,0 +1,22 @@
+[Desktop Entry]
+Name=WebInterface
+Name[bg]=Уеб интерфейс
+Name[da]=Web-grænseflade
+Name[de]=Web-Schnittstelle
+Name[et]=Veebiliides
+Name[it]=Interfaccia Web
+Name[nb]=Internettgrensesnitt
+Name[nds]=Nettkoppelsteed
+Name[nl]=Webinterface
+Name[pl]=Interfejs WWW
+Name[pt]=Interface Web
+Name[sr]=Веб интерфејс
+Name[sr@Latn]=Veb interfejs
+Name[sv]=Webb-gränssnitt
+Name[tr]=Ağ Arayüzü
+Name[uk]=Веб-інтерфейс
+Name[xx]=xxWebInterfacexx
+Name[zh_CN]=Web 界面
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktwebinterfaceplugin
diff --git a/plugins/webinterface/ktwebinterfaceplugin.kcfg b/plugins/webinterface/ktwebinterfaceplugin.kcfg
new file mode 100644
index 0000000..cbfd5e7
--- /dev/null
+++ b/plugins/webinterface/ktwebinterfaceplugin.kcfg
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <kcfgfile name="ktwebinterfacepluginrc"/>
+ <group name="general">
+ <entry name="port" type="Int">
+ <label>Port number</label>
+ <default>8080</default>
+ </entry>
+
+ <entry name="forward" type="Bool">
+ <label>forward Port</label>
+ <default>false</default>
+ </entry>
+
+ <entry name="sessionTTL" type ="Int">
+ <label>Session time to live</label>
+ <default>3600</default>
+ </entry>
+ <entry name="skin" type ="String">
+ <label>interface skin</label>
+ <default>default</default>
+ </entry>
+ <entry name="phpExecutablePath" type ="String">
+ <label>php executable path</label>
+ <default></default>
+ </entry>
+
+ <entry name="username" type ="String">
+ <label>username</label>
+ <default></default>
+ </entry>
+ <entry name="password" type ="String">
+ <label>password</label>
+ <default></default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/webinterface/php_handler.cpp b/plugins/webinterface/php_handler.cpp
new file mode 100644
index 0000000..d04c3b6
--- /dev/null
+++ b/plugins/webinterface/php_handler.cpp
@@ -0,0 +1,121 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "php_handler.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <util/log.h>
+#include "php_interface.h"
+
+
+using namespace kt;
+using namespace bt;
+
+namespace kt
+{
+ QMap<QString,QByteArray> PhpHandler::scripts;
+
+ PhpHandler::PhpHandler(const QString & php_exe,PhpInterface *php) : QProcess(php_exe),php_i(php)
+ {
+ connect(this,SIGNAL(readyReadStdout()),this,SLOT(onReadyReadStdout()));
+ connect(this,SIGNAL(processExited()),this,SLOT(onExited()));
+ }
+
+ PhpHandler::~PhpHandler()
+ {
+ }
+
+ bool PhpHandler::executeScript(const QString & path,const QMap<QString,QString> & args)
+ {
+ QByteArray php_s;
+ if (!scripts.contains(path))
+ {
+ QFile fptr(path);
+ if (!fptr.open(IO_ReadOnly))
+ {
+ Out(SYS_WEB|LOG_DEBUG) << "Failed to open " << path << endl;
+ return false;
+ }
+ php_s = fptr.readAll();
+ scripts.insert(path,php_s);
+ }
+ else
+ {
+ php_s = scripts[path];
+ }
+
+ output.resize(0);
+
+ int firstphptag = QCString(php_s).find("<?php");
+ if (firstphptag == -1)
+ return false;
+
+ int off = firstphptag + 6;
+ QByteArray data;
+ QTextStream ts(data,IO_WriteOnly);
+ ts.setEncoding( QTextStream::UnicodeUTF8 );
+ ts.writeRawBytes(php_s.data(),off); // first write the opening tag from the script
+ php_i->globalInfo(ts);
+ php_i->downloadStatus(ts);
+
+ QMap<QString,QString>::const_iterator it;
+
+ for ( it = args.begin(); it != args.end(); ++it )
+ {
+ ts << QString("$_REQUEST['%1']=\"%2\";\n").arg(it.key()).arg(it.data());
+ }
+ ts.writeRawBytes(php_s.data() + off,php_s.size() - off); // the rest of the script
+ ts << flush;
+
+#if 0
+ QFile dinges("output.php");
+ if (dinges.open(IO_WriteOnly))
+ {
+ QTextStream out(&dinges);
+ out.writeRawBytes(data.data(),data.size());
+ dinges.close();
+ }
+#endif
+ return launch(data);
+ }
+
+ void PhpHandler::onExited()
+ {
+ // read remaining data
+ onReadyReadStdout();
+ finished();
+ }
+
+ void PhpHandler::onReadyReadStdout()
+ {
+ QTextStream out(output,IO_WriteOnly|IO_Append);
+ while (canReadLineStdout())
+ {
+ QByteArray d = readStdout();
+ out.writeRawBytes(d.data(),d.size());
+ }
+ }
+
+}
+
+#include "php_handler.moc"
diff --git a/plugins/webinterface/php_handler.h b/plugins/webinterface/php_handler.h
new file mode 100644
index 0000000..b9bfcb6
--- /dev/null
+++ b/plugins/webinterface/php_handler.h
@@ -0,0 +1,57 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef PHP_HANDLER_H
+#define PHP_HANDLER_H
+
+#include <qmap.h>
+#include <kurl.h>
+#include <qprocess.h>
+
+
+namespace kt
+{
+ class PhpInterface;
+
+ class PhpHandler : public QProcess
+ {
+ Q_OBJECT
+ public:
+ PhpHandler(const QString & php_exe,PhpInterface *php);
+ virtual ~PhpHandler();
+
+ bool executeScript(const QString & path,const QMap<QString,QString> & args);
+ const QByteArray & getOutput() const {return output;};
+
+ public slots:
+ void onExited();
+ void onReadyReadStdout();
+
+ signals:
+ void finished();
+
+ private:
+ QByteArray output;
+ PhpInterface *php_i;
+
+ static QMap<QString,QByteArray> scripts;
+ };
+}
+
+#endif
diff --git a/plugins/webinterface/php_interface.cpp b/plugins/webinterface/php_interface.cpp
new file mode 100644
index 0000000..8ee7d0b
--- /dev/null
+++ b/plugins/webinterface/php_interface.cpp
@@ -0,0 +1,486 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <kio/global.h>
+#include <kglobal.h>
+#include <klocale.h>
+
+#include <net/socketmonitor.h>
+#include <torrent/choker.h>
+#include <torrent/udptrackersocket.h>
+#include <kademlia/dhtbase.h>
+#include <torrent/server.h>
+#include <util/log.h>
+#include <interfaces/functions.h>
+
+#include "php_interface.h"
+
+using namespace bt;
+
+namespace kt
+{
+ extern QString DataDir();
+
+ using bt::FIRST_PRIORITY;
+ using bt::NORMAL_PRIORITY;
+ using bt::LAST_PRIORITY;
+ using bt::EXCLUDED;
+
+
+ QString BytesToString2(Uint64 bytes,int precision = 2)
+ {
+ KLocale* loc = KGlobal::locale();
+ if (bytes >= 1024 * 1024 * 1024)
+ return QString("%1 GB").arg(loc->formatNumber(bytes / TO_GIG,precision < 0 ? 2 : precision));
+ else if (bytes >= 1024*1024)
+ return QString("%1 MB").arg(loc->formatNumber(bytes / TO_MEG,precision < 0 ? 1 : precision));
+ else if (bytes >= 1024)
+ return QString("%1 KB").arg(loc->formatNumber(bytes / TO_KB,precision < 0 ? 1 : precision));
+ else
+ return QString("%1 B").arg(bytes);
+ }
+
+ QString KBytesPerSecToString2(double speed,int precision = 2)
+ {
+ KLocale* loc = KGlobal::locale();
+ return QString("%1 KB/s").arg(loc->formatNumber(speed,precision));
+ }
+
+ /************************
+ *PhpCodeGenerator *
+ ************************/
+ PhpCodeGenerator::PhpCodeGenerator(CoreInterface *c)
+ {
+ core=c;
+ }
+
+ /*Generate php code
+ * function downloadStatus()
+ * {
+ * return array( ... );
+ * }
+ */
+ void PhpCodeGenerator::downloadStatus(QTextStream & out)
+ {
+ TorrentStats stats;
+ //Priority file_priority;
+ QString status;
+ out << "function downloadStatus()\n{\nreturn array(";
+
+ QPtrList<TorrentInterface>::iterator i= core->getQueueManager()->begin();
+ for(int k=0; i != core->getQueueManager()->end(); i++, k++)
+ {
+ if (k > 0)
+ out << ",\n";
+
+ stats=(*i)->getStats();
+ out << QString("\n%1 => array(").arg(k);
+
+ out << QString("\"imported_bytes\" => %1,\n").arg(stats.imported_bytes);
+ out << QString("\"bytes_downloaded\" => \"%1\",\n").arg(BytesToString2(stats.bytes_downloaded));
+ out << QString("\"bytes_uploaded\" => \"%1\",\n").arg(BytesToString2(stats.bytes_uploaded));
+ out << QString("\"bytes_left\" => %1,\n").arg(stats.bytes_left);
+ out << QString("\"bytes_left_to_download\" => %1,\n").arg(stats.bytes_left_to_download);
+ out << QString("\"total_bytes\" => \"%1\",\n").arg(BytesToString2(stats.total_bytes));
+ out << QString("\"total_bytes_to_download\" => %1,\n").arg(stats.total_bytes_to_download);
+ out << QString("\"download_rate\" => \"%1\",\n").arg(KBytesPerSecToString2(stats.download_rate / 1024.0));
+ out << QString("\"upload_rate\" => \"%1\",\n").arg(KBytesPerSecToString2(stats.upload_rate / 1024.0));
+ out << QString("\"num_peers\" => %1,\n").arg(stats.num_peers);
+ out << QString("\"num_chunks_downloading\" => %1,\n").arg(stats.num_chunks_downloading);
+ out << QString("\"total_chunks\" => %1,\n").arg(stats.total_chunks);
+ out << QString("\"num_chunks_downloaded\" => %1,\n").arg(stats.num_chunks_downloaded);
+ out << QString("\"num_chunks_excluded\" => %1,\n").arg(stats.num_chunks_excluded);
+ out << QString("\"chunk_size\" => %1,\n").arg(stats.chunk_size);
+ out << QString("\"seeders_total\" => %1,\n").arg(stats.seeders_total);
+ out << QString("\"seeders_connected_to\" => %1,\n").arg(stats.seeders_connected_to);
+ out << QString("\"leechers_total\" => %1,\n").arg(stats.leechers_total);
+ out << QString("\"leechers_connected_to\" => %1,\n").arg(stats.leechers_connected_to);
+ out << QString("\"status\" => %1,\n").arg(stats.status);
+ out << QString("\"running\" => %1,\n").arg(stats.running);
+ out << QString("\"trackerstatus\" => \"%1\",\n").arg(stats.trackerstatus.replace("\\", "\\\\").replace("\"", "\\\"").replace("$", "\\$"));
+ out << QString("\"session_bytes_downloaded\" => %1,\n").arg(stats.session_bytes_downloaded);
+ out << QString("\"session_bytes_uploaded\" => %1,\n").arg(stats.session_bytes_uploaded);
+ out << QString("\"trk_bytes_downloaded\" => %1,\n").arg(stats.trk_bytes_downloaded);
+ out << QString("\"trk_bytes_uploaded\" => %1,\n").arg(stats.trk_bytes_uploaded);
+ out << QString("\"torrent_name\" => \"%1\",\n").arg(stats.torrent_name.replace("\\", "\\\\").replace("\"", "\\\"").replace("$", "\\$"));
+ out << QString("\"output_path\" => \"%1\",\n").arg(stats.output_path.replace("\\", "\\\\").replace("\"", "\\\"").replace("$", "\\$"));
+ out << QString("\"stopped_by_error\" => \"%1\",\n").arg(stats.stopped_by_error);
+ out << QString("\"completed\" => \"%1\",\n").arg(stats.completed);
+ out << QString("\"user_controlled\" => \"%1\",\n").arg(stats.user_controlled);
+ out << QString("\"max_share_ratio\" => %1,\n").arg(stats.max_share_ratio);
+ out << QString("\"priv_torrent\" => \"%1\",\n").arg(stats.priv_torrent);
+ out << QString("\"num_files\" => \"%1\",\n").arg((*i)->getNumFiles());
+ out << QString("\"files\" => array(");
+ out << flush;
+ if (stats.multi_file_torrent)
+ {
+ //for loop to add each file+status to "files" array
+ for (Uint32 j = 0;j < (*i)->getNumFiles();j++)
+ {
+ if (j > 0)
+ out << ",\n";
+
+ TorrentFileInterface & file = (*i)->getTorrentFile(j);
+ out << QString("\"%1\" => array(\n").arg(j);
+ out << QString("\"name\" => \"%1\",\n").arg(file.getPath());
+ out << QString("\"size\" => \"%1\",\n").arg(KIO::convertSize(file.getSize()));
+ out << QString("\"perc_done\" => \"%1\",\n").arg(file.getDownloadPercentage());
+ out << QString("\"status\" => \"%1\"\n").arg(file.getPriority());
+ out << QString(")\n");
+ out << flush;
+ }
+ }
+
+ out << ")\n";
+ out << ")\n";
+ }
+
+ out << ");\n}\n";
+ }
+
+ /*Generate php code
+ * function globalStatus()
+ * {
+ * return array( ... );
+ * }
+ */
+ void PhpCodeGenerator::globalInfo(QTextStream & out)
+ {
+ out << "function globalInfo()\n{\nreturn array(";
+ CurrentStats stats=core->getStats();
+
+ out << QString("\"download_speed\" => \"%1\",").arg(KBytesPerSecToString2(stats.download_speed / 1024.0));
+ out << QString("\"upload_speed\" => \"%1\",").arg(KBytesPerSecToString2(stats.upload_speed / 1024.0));
+ out << QString("\"bytes_downloaded\" => \"%1\",").arg(stats.bytes_downloaded);
+ out << QString("\"bytes_uploaded\" => \"%1\",").arg(stats.bytes_uploaded);
+ out << QString("\"max_download_speed\" => \"%1\",").arg(core->getMaxDownloadSpeed());
+ out << QString("\"max_upload_speed\" => \"%1\",").arg(core->getMaxUploadSpeed());
+ out << QString("\"max_downloads\" => \"%1\",").arg(Settings::maxDownloads());
+ out << QString("\"max_seeds\"=> \"%1\",").arg(Settings::maxSeeds());
+ out << QString("\"dht_support\" => \"%1\",").arg(Settings::dhtSupport());
+ out << QString("\"use_encryption\" => \"%1\"").arg(Settings::useEncryption());
+ out << ");\n}\n";
+ }
+
+
+ /************************
+ *PhpActionExec *
+ ************************/
+ PhpActionExec::PhpActionExec(CoreInterface *c)
+ {
+ core=c;
+ }
+
+ bool PhpActionExec::exec(KURL & url,bool & shutdown)
+ {
+ bool ret = false;
+ shutdown = false;
+ int separator_loc;
+ QString parse;
+ QString torrent_num;
+ QString file_num;
+ KURL redirected_url;
+ redirected_url.setPath(url.path());
+
+ const QMap<QString, QString> & params = url.queryItems();
+ QMap<QString, QString>::ConstIterator it;
+
+ for ( it = params.begin(); it != params.end(); ++it )
+ {
+ // Out(SYS_WEB| LOG_DEBUG) << "exec " << it.key().latin1() << endl;
+ switch(it.key()[0])
+ {
+ case 'd':
+ if(it.key()=="dht")
+ {
+ if(it.data()=="start")
+ {
+ Settings::setDhtSupport(true);
+ }
+ else
+ {
+ Settings::setDhtSupport(false);
+ }
+
+ dht::DHTBase & ht = Globals::instance().getDHT();
+ if (Settings::dhtSupport() && !ht.isRunning())
+ {
+ ht.start(kt::DataDir() + "dht_table",kt::DataDir() + "dht_key",Settings::dhtPort());
+ ret = true;
+ }
+ else if (!Settings::dhtSupport() && ht.isRunning())
+ {
+ ht.stop();
+ ret = true;
+ }
+ else if (Settings::dhtSupport() && ht.getPort() != Settings::dhtPort())
+ {
+ ht.stop();
+ ht.start(kt::DataDir() + "dht_table",kt::DataDir() + "dht_key",Settings::dhtPort());
+ ret = true;
+ }
+ }
+ break;
+ case 'e':
+ if(it.key()=="encription")
+ {
+ if(it.data()=="start")
+ {
+ Settings::setUseEncryption(true);
+ }
+ else
+ {
+ Settings::setUseEncryption(false);
+ }
+
+ if (Settings::useEncryption())
+ {
+ Globals::instance().getServer().enableEncryption(Settings::allowUnencryptedConnections());
+ }
+ else
+ {
+ Globals::instance().getServer().disableEncryption();
+ }
+ ret = true;
+ }
+ break;
+ case 'f':
+ //parse argument into torrent number and file number
+ separator_loc=it.data().find('-');
+ parse=it.data();
+ torrent_num.append(parse.left(separator_loc));
+ file_num.append(parse.right(parse.length()-(separator_loc+1)));
+
+ if(it.key()=="file_lp")
+ {
+ QPtrList<TorrentInterface>::iterator i= core->getQueueManager()->begin();
+ for(int k=0; i != core->getQueueManager()->end(); i++, k++)
+ {
+ if(torrent_num.toInt()==k)
+ {
+ TorrentFileInterface & file = (*i)->getTorrentFile(file_num.toInt());
+ file.setPriority(LAST_PRIORITY);
+ ret = true;
+ break;
+ }
+ }
+ }
+ else if(it.key()=="file_np")
+ {
+ QPtrList<TorrentInterface>::iterator i= core->getQueueManager()->begin();
+ for(int k=0; i != core->getQueueManager()->end(); i++, k++)
+ {
+ if(torrent_num.toInt()==k)
+ {
+ TorrentFileInterface & file = (*i)->getTorrentFile(file_num.toInt());
+ file.setPriority(NORMAL_PRIORITY);
+ ret = true;
+ break;
+ }
+ }
+ }
+ else if(it.key()=="file_hp")
+ {
+ QPtrList<TorrentInterface>::iterator i= core->getQueueManager()->begin();
+ for(int k=0; i != core->getQueueManager()->end(); i++, k++)
+ {
+ if(torrent_num.toInt()==k)
+ {
+ TorrentFileInterface & file = (*i)->getTorrentFile(file_num.toInt());
+ file.setPriority(FIRST_PRIORITY);
+ ret = true;
+ break;
+ }
+ }
+ }
+ else if(it.key()=="file_stop")
+ {
+ QPtrList<TorrentInterface>::iterator i= core->getQueueManager()->begin();
+ for(int k=0; i != core->getQueueManager()->end(); i++, k++)
+ {
+ if(torrent_num.toInt()==k)
+ {
+ TorrentFileInterface & file = (*i)->getTorrentFile(file_num.toInt());
+ file.setPriority(ONLY_SEED_PRIORITY);
+ ret = true;
+ break;
+ }
+ }
+ }
+ break;
+ case 'g':
+ if(it.key()=="global_connection")
+ {
+ Settings::setMaxTotalConnections(it.data().toInt());
+ PeerManager::setMaxTotalConnections(Settings::maxTotalConnections());
+ ret = true;
+ }
+ break;
+ case 'l':
+ if(it.key()=="load_torrent" && it.data().length() > 0)
+ {
+ core->loadSilently(KURL::decode_string(it.data()));
+ ret = true;
+ }
+ break;
+ case 'm':
+ if(it.key()=="maximum_downloads")
+ {
+ core->setMaxDownloads(it.data().toInt());
+ Settings::setMaxDownloads(it.data().toInt());
+ ret = true;
+ }
+ else if(it.key()=="maximum_seeds")
+ {
+ core->setMaxSeeds(it.data().toInt());
+ Settings::setMaxSeeds(it.data().toInt());
+ ret = true;
+ }
+ else if(it.key()=="maximum_connection_per_torrent")
+ {
+ PeerManager::setMaxConnections(it.data().toInt());
+ Settings::setMaxConnections(it.data().toInt());
+ ret = true;
+ }
+ else if(it.key()=="maximum_upload_rate")
+ {
+ Settings::setMaxUploadRate(it.data().toInt());
+ core->setMaxUploadSpeed(Settings::maxUploadRate());
+ net::SocketMonitor::setUploadCap( Settings::maxUploadRate() * 1024);
+ ret = true;
+ }
+ else if(it.key()=="maximum_download_rate")
+ {
+ Settings::setMaxDownloadRate(it.data().toInt());
+ core->setMaxDownloadSpeed(Settings::maxDownloadRate());
+ net::SocketMonitor::setDownloadCap(Settings::maxDownloadRate()*1024);
+ ret = true;
+ }
+ else if(it.key()=="maximum_share_ratio")
+ {
+ Settings::setMaxRatio(it.data().toInt());
+ ret = true;
+ }
+ break;
+ case 'n':
+ if(it.key()=="number_of_upload_slots")
+ {
+ Settings::setNumUploadSlots(it.data().toInt());
+ Choker::setNumUploadSlots(Settings::numUploadSlots());
+ ret = true;
+ }
+ break;
+ case 'p':
+ if(it.key()=="port")
+ {
+ Settings::setPort(it.data().toInt());
+ core->changePort(Settings::port());
+ }
+ else if(it.key()=="port_udp_tracker")
+ {
+ Settings::setUdpTrackerPort(it.data().toInt());
+ UDPTrackerSocket::setPort(Settings::udpTrackerPort());
+ ret = true;
+ }
+ break;
+ case 'q':
+ if(it.key()=="quit" && !it.data().isEmpty())
+ {
+ shutdown = true;
+ ret = true;
+ }
+ break;
+ case 'r':
+ if(it.key()=="remove")
+ {
+ QPtrList<TorrentInterface>::iterator i= core->getQueueManager()->begin();
+ for(int k=0; i != core->getQueueManager()->end(); i++, k++)
+ {
+ if(it.data().toInt()==k)
+ {
+ core->remove((*i), false);
+ ret = true;
+ break;
+ }
+ }
+ }
+ break;
+ case 's':
+ if(it.key()=="stopall" && !it.data().isEmpty())
+ {
+ core->stopAll(3);
+ }
+ else if(it.key()=="startall" && !it.data().isEmpty())
+ {
+ core->startAll(3);
+ }
+ else if(it.key()=="stop")
+ {
+ QPtrList<TorrentInterface>::iterator i= core->getQueueManager()->begin();
+ for(int k=0; i != core->getQueueManager()->end(); i++, k++)
+ {
+ if(it.data().toInt()==k)
+ {
+ (*i)->stop(true);
+ ret = true;
+ break;
+ }
+ }
+ }
+ else if(it.key()=="start")
+ {
+ QPtrList<TorrentInterface>::iterator i= core->getQueueManager()->begin();
+ for(int k=0; i != core->getQueueManager()->end(); i++, k++)
+ {
+ if(it.data().toInt()==k)
+ {
+ (*i)->start();
+ ret = true;
+ break;
+ }
+ }
+ }
+ break;
+
+ default:
+ // add unknown query items to the redirected url
+ // we don't add the keys above, because if the user presses refresh
+ // the same action will be taken again
+ redirected_url.addQueryItem(it.key(),it.data());
+ break;
+ }
+ Settings::writeConfig();
+ }
+
+ if (ret)
+ url = redirected_url;
+
+ return ret;
+ }
+
+ /************************
+ *PhpInterface *
+ ************************/
+ PhpInterface::PhpInterface(CoreInterface *c):PhpCodeGenerator(c), PhpActionExec(c)
+ {
+
+ }
+
+}
diff --git a/plugins/webinterface/php_interface.h b/plugins/webinterface/php_interface.h
new file mode 100644
index 0000000..be79019
--- /dev/null
+++ b/plugins/webinterface/php_interface.h
@@ -0,0 +1,68 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef PHP_INTERFACE_H
+#define PHP_INTERFACE_H
+
+#include <qstring.h>
+#include <torrent/peermanager.h>
+#include <settings.h>
+#include <torrent/queuemanager.h>
+#include <interfaces/coreinterface.h>
+#include <interfaces/torrentinterface.h>
+#include <interfaces/torrentfileinterface.h>
+
+ /**
+ * @author Diego R. Brogna
+ */
+namespace kt
+{
+ class PhpCodeGenerator
+ {
+ public:
+ PhpCodeGenerator(CoreInterface *c);
+ virtual ~PhpCodeGenerator(){}
+
+ void downloadStatus(QTextStream & out);
+ void globalInfo(QTextStream & out);
+ private:
+ CoreInterface *core;
+ };
+
+ class PhpActionExec
+ {
+ public:
+ PhpActionExec(CoreInterface *c);
+ virtual ~PhpActionExec(){};
+
+ bool exec(KURL & url,bool & shutdown);
+ private:
+ CoreInterface *core;
+ };
+
+ class PhpInterface: public PhpCodeGenerator, public PhpActionExec
+ {
+ public:
+ PhpInterface(CoreInterface *c);
+ //~PhpInterface{};
+ };
+}
+
+#endif
diff --git a/plugins/webinterface/webinterfaceplugin.cpp b/plugins/webinterface/webinterfaceplugin.cpp
new file mode 100644
index 0000000..bce4115
--- /dev/null
+++ b/plugins/webinterface/webinterfaceplugin.cpp
@@ -0,0 +1,128 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+#include <kglobal.h>
+
+#include <util/log.h>
+#include <interfaces/coreinterface.h>
+#include <interfaces/guiinterface.h>
+#include <interfaces/torrentinterface.h>
+#include <torrent/globals.h>
+#include <net/portlist.h>
+#include "webinterfaceprefpage.h"
+#include "webinterfaceplugin.h"
+#include "httpserver.h"
+#include "webinterfacepluginsettings.h"
+
+#define NAME "Web Interface"
+#define AUTHOR "Diego R. Brogna"
+#define EMAIL "dierbro@gmail.com"
+
+K_EXPORT_COMPONENT_FACTORY(ktwebinterfaceplugin,KGenericFactory<kt::WebInterfacePlugin>("ktwebinterfaceplugin"))
+
+using namespace bt;
+namespace kt
+{
+ WebInterfacePlugin::WebInterfacePlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name, args,NAME,i18n("Web Interface"),AUTHOR,EMAIL,i18n("Allow to control ktorrent through browser"),"toggle_log")
+ {
+ http_server = 0;
+ pref=0;
+ }
+
+ WebInterfacePlugin::~WebInterfacePlugin()
+ {
+
+ }
+
+ void WebInterfacePlugin::load()
+ {
+ initServer();
+
+ pref = new WebInterfacePrefPage(this);
+ getGUI()->addPrefPage(pref);
+
+ }
+
+ void WebInterfacePlugin::unload()
+ {
+ if (http_server)
+ {
+ bt::Globals::instance().getPortList().removePort(http_server->port(),net::TCP);
+ delete http_server;
+ http_server = 0;
+ }
+
+ getGUI()->removePrefPage(pref);
+ delete pref;
+ pref = 0;
+ }
+
+ void WebInterfacePlugin::initServer()
+ {
+ bt::Uint16 port = WebInterfacePluginSettings::port();
+ bt::Uint16 i = 0;
+
+ while (i < 10)
+ {
+ http_server = new HttpServer(getCore(),port + i);
+ if (!http_server->ok())
+ {
+ delete http_server;
+ http_server = 0;
+ }
+ else
+ break;
+ i++;
+ }
+
+ if (http_server)
+ {
+ if(WebInterfacePluginSettings::forward())
+ bt::Globals::instance().getPortList().addNewPort(http_server->port(),net::TCP,true);
+ Out(SYS_WEB|LOG_ALL) << "Web server listen on port "<< http_server->port() << endl;
+ }
+ else
+ {
+ Out(SYS_WEB|LOG_ALL) << "Cannot bind to port " << port <<" or the 10 following ports. WebInterface plugin cannot be loaded." << endl;
+ return;
+ }
+ }
+
+ void WebInterfacePlugin::preferencesUpdated()
+ {
+ if( http_server && http_server->port() != WebInterfacePluginSettings::port())
+ {
+ //stop and delete http server
+ bt::Globals::instance().getPortList().removePort(http_server->port(),net::TCP);
+ delete http_server;
+ http_server = 0;
+ // reinitialize server
+ initServer();
+ }
+ }
+
+ bool WebInterfacePlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+}
+
+#include "webinterfaceplugin.moc"
diff --git a/plugins/webinterface/webinterfaceplugin.h b/plugins/webinterface/webinterfaceplugin.h
new file mode 100644
index 0000000..469fda2
--- /dev/null
+++ b/plugins/webinterface/webinterfaceplugin.h
@@ -0,0 +1,54 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef KTWEBINTERFACEPLUGIN_H
+#define KTWEBINTERFACEPLUGIN_H
+
+#include <interfaces/plugin.h>
+
+namespace kt
+{
+ /**
+ * @author Diego R. Brogna
+ */
+ class HttpServer;
+
+ class WebInterfacePlugin : public Plugin
+ {
+ Q_OBJECT
+ public:
+ WebInterfacePlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~WebInterfacePlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual bool versionCheck(const QString& version) const;
+
+ void preferencesUpdated();
+ private:
+ void initServer();
+
+ WebInterfacePrefPage* pref;
+ HttpServer* http_server;
+ };
+
+}
+
+#endif
diff --git a/plugins/webinterface/webinterfacepluginsettings.kcfgc b/plugins/webinterface/webinterfacepluginsettings.kcfgc
new file mode 100644
index 0000000..fd5aebf
--- /dev/null
+++ b/plugins/webinterface/webinterfacepluginsettings.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=ktwebinterfaceplugin.kcfg
+ClassName=WebInterfacePluginSettings
+Namespace=kt
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables
diff --git a/plugins/webinterface/webinterfacepref.ui b/plugins/webinterface/webinterfacepref.ui
new file mode 100644
index 0000000..63d368b
--- /dev/null
+++ b/plugins/webinterface/webinterfacepref.ui
@@ -0,0 +1,256 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>WebInterfacePreference</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>WebInterfacePreference</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>666</width>
+ <height>883</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>500</width>
+ <height>350</height>
+ </size>
+ </property>
+ <property name="caption">
+ <string>Search Preferences</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup" row="0" column="0">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Web Server</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>port</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>8080</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>forward</cstring>
+ </property>
+ <property name="text">
+ <string>Forward port</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>54</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Session TTL (in sec):</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>sessionTTL</cstring>
+ </property>
+ <property name="maxValue">
+ <number>100000</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="lineStep">
+ <number>100</number>
+ </property>
+ <property name="value">
+ <number>3600</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Select interface:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>interfaceSkinBox</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>username</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>btnPassword</cstring>
+ </property>
+ <property name="text">
+ <string>Change password ...</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Php executable path:</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>phpExecutablePath</cstring>
+ </property>
+ </widget>
+ <widget class="KLed">
+ <property name="name">
+ <cstring>kled</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>btnPassword</sender>
+ <signal>clicked()</signal>
+ <receiver>WebInterfacePreference</receiver>
+ <slot>btnUpdate_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>phpExecutablePath</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>WebInterfacePreference</receiver>
+ <slot>changeLedState()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>btnUpdate_clicked()</slot>
+ <slot>changeLedState()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kled.h</includehint>
+</includehints>
+</UI>
diff --git a/plugins/webinterface/webinterfaceprefpage.cpp b/plugins/webinterface/webinterfaceprefpage.cpp
new file mode 100644
index 0000000..20dbc97
--- /dev/null
+++ b/plugins/webinterface/webinterfaceprefpage.cpp
@@ -0,0 +1,60 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "webinterfaceprefpage.h"
+#include "webinterfaceplugin.h"
+namespace kt
+{
+
+ WebInterfacePrefPage::WebInterfacePrefPage(WebInterfacePlugin* plugin)
+ : PrefPageInterface(i18n("WebInterface"), i18n("WebInterface Options"),
+ KGlobal::iconLoader()->loadIcon("toggle_log",KIcon::NoGroup))
+ {
+ m_widget = 0;
+ w_plugin=plugin;
+ }
+
+
+ WebInterfacePrefPage::~WebInterfacePrefPage()
+ {}
+
+ bool WebInterfacePrefPage::apply()
+ {
+ if(m_widget)
+ m_widget->apply();
+ w_plugin->preferencesUpdated();
+ return true;
+ }
+
+ void WebInterfacePrefPage::createWidget(QWidget* parent)
+ {
+ m_widget = new WebInterfacePrefWidget(parent);
+ }
+
+ void WebInterfacePrefPage::updateData()
+ {
+ }
+
+ void WebInterfacePrefPage::deleteWidget()
+ {
+ if(m_widget)
+ delete m_widget;
+ }
+}
diff --git a/plugins/webinterface/webinterfaceprefpage.h b/plugins/webinterface/webinterfaceprefpage.h
new file mode 100644
index 0000000..a10f1c9
--- /dev/null
+++ b/plugins/webinterface/webinterfaceprefpage.h
@@ -0,0 +1,55 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef WEBINTERFACEPREFPAGE_H
+#define WEBINTERFACEPREFPAGE_H
+#include <interfaces/prefpageinterface.h>
+#include "webinterfaceprefwidget.h"
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+namespace kt
+{
+
+ /**
+ * WebInterface plugin preferences page
+ * @author Diego R. Brogna <dierbro@gmail.com>
+ */
+ class WebInterfacePlugin;
+ class WebInterfacePrefPage : public PrefPageInterface
+ {
+ public:
+ WebInterfacePrefPage(WebInterfacePlugin* plugin);
+ virtual ~WebInterfacePrefPage();
+
+ virtual bool apply();
+ virtual void createWidget(QWidget* parent);
+ virtual void updateData();
+ virtual void deleteWidget();
+
+ private:
+ WebInterfacePrefWidget* m_widget;
+ WebInterfacePlugin* w_plugin;
+ };
+
+}
+
+#endif
diff --git a/plugins/webinterface/webinterfaceprefwidget.cpp b/plugins/webinterface/webinterfaceprefwidget.cpp
new file mode 100644
index 0000000..fc11d89
--- /dev/null
+++ b/plugins/webinterface/webinterfaceprefwidget.cpp
@@ -0,0 +1,137 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "webinterfaceprefwidget.h"
+#include "webinterfacepluginsettings.h"
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+
+#include <qwidget.h>
+#include <qstring.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <knuminput.h>
+#include <kurlrequester.h>
+#include <klineedit.h>
+#include <kpassdlg.h>
+#include <kmdcodec.h>
+#include <kled.h>
+#include <qtooltip.h>
+
+#include <net/portlist.h>
+#include <torrent/globals.h>
+using namespace bt;
+namespace kt
+{
+
+WebInterfacePrefWidget::WebInterfacePrefWidget(QWidget *parent, const char *name):WebInterfacePreference(parent,name)
+{
+ port->setValue(WebInterfacePluginSettings::port());
+ forward->setChecked(WebInterfacePluginSettings::forward());
+ sessionTTL->setValue(WebInterfacePluginSettings::sessionTTL());
+
+ QStringList dirList=KGlobal::instance()->dirs()->findDirs("data", "ktorrent/www");
+ QDir d(*(dirList.begin()));
+ QStringList skinList=d.entryList(QDir::Dirs);
+ for ( QStringList::Iterator it = skinList.begin(); it != skinList.end(); ++it ){
+ if(*it=="." || *it=="..")
+ continue;
+ interfaceSkinBox->insertItem(*it);
+ }
+
+ interfaceSkinBox->setCurrentText (WebInterfacePluginSettings::skin());
+
+ if(WebInterfacePluginSettings::phpExecutablePath().isEmpty()){
+ QString phpPath=KStandardDirs::findExe("php");
+ if(phpPath==QString::null)
+ phpPath=KStandardDirs::findExe("php-cli");
+
+ if(phpPath==QString::null)
+ phpExecutablePath->setURL (i18n("Php executable is not in default path, please enter the path manually"));
+ else
+ phpExecutablePath->setURL (phpPath);
+ }
+ else
+ phpExecutablePath->setURL (WebInterfacePluginSettings::phpExecutablePath());
+
+ username->setText(WebInterfacePluginSettings::username());
+}
+
+bool WebInterfacePrefWidget::apply()
+{
+ if(WebInterfacePluginSettings::port()==port->value()){
+ if(forward->isChecked())
+ bt::Globals::instance().getPortList().addNewPort(port->value(),net::TCP,true);
+ else
+ bt::Globals::instance().getPortList().removePort(port->value(),net::TCP);
+ }
+ WebInterfacePluginSettings::setPort(port->value () );
+ WebInterfacePluginSettings::setForward(forward->isChecked());
+ WebInterfacePluginSettings::setSessionTTL(sessionTTL->value () );
+ WebInterfacePluginSettings::setSkin(interfaceSkinBox->currentText());
+ WebInterfacePluginSettings::setPhpExecutablePath(phpExecutablePath->url () );
+ if(!username->text().isEmpty() && !password.isEmpty()){
+ WebInterfacePluginSettings::setUsername(username->text() );
+ KMD5 context(password);
+ WebInterfacePluginSettings::setPassword(context.hexDigest().data());
+ }
+
+ WebInterfacePluginSettings::writeConfig();
+ return true;
+}
+
+void WebInterfacePrefWidget::btnUpdate_clicked()
+{
+ QCString passwd;
+ int result = KPasswordDialog::getNewPassword(passwd, i18n("Please enter a new password for the web interface."));
+ if (result == KPasswordDialog::Accepted)
+ password=passwd;
+
+}
+
+void WebInterfacePrefWidget::changeLedState()
+{
+ QFileInfo fi(phpExecutablePath->url());
+ if(fi.isExecutable() && (fi.isFile() || fi.isSymLink())){
+ QToolTip::add( kled, i18n("%1 exists and it is executable").arg(phpExecutablePath->url()));
+ kled->setColor(green);
+ }
+ else if (!fi.exists()){
+ QToolTip::add( kled, i18n("%1 does not exist").arg(phpExecutablePath->url()) );
+ kled->setColor(red);
+ }
+ else if (!fi.isExecutable()){
+ QToolTip::add( kled, i18n("%1 is not executable").arg(phpExecutablePath->url()) );
+ kled->setColor(red);
+ }
+ else if (fi.isDir()){
+ QToolTip::add( kled, i18n("%1 is a directory").arg(phpExecutablePath->url()) );
+ kled->setColor(red);
+ }
+ else{
+ QToolTip::add( kled, i18n("%1 is not php executable path").arg(phpExecutablePath->url()) );
+ kled->setColor(red);
+ }
+}
+}
+#include "webinterfaceprefwidget.moc"
diff --git a/plugins/webinterface/webinterfaceprefwidget.h b/plugins/webinterface/webinterfaceprefwidget.h
new file mode 100644
index 0000000..b328efe
--- /dev/null
+++ b/plugins/webinterface/webinterfaceprefwidget.h
@@ -0,0 +1,41 @@
+ /***************************************************************************
+ * Copyright (C) 2006 by Diego R. Brogna *
+ * dierbro@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef WEBINTERFACEPREFWIDGET_H
+#define WEBINTERFACEPREFWIDGET_H
+
+#include "webinterfacepref.h"
+namespace kt
+{
+
+ class WebInterfacePrefWidget:public WebInterfacePreference
+ {
+ Q_OBJECT
+ public:
+ WebInterfacePrefWidget(QWidget *parent = 0, const char *name = 0);
+ bool apply();
+ QCString password;
+ public slots:
+ void btnUpdate_clicked();
+ void changeLedState();
+ };
+
+}
+#endif
diff --git a/plugins/webinterface/www/Makefile.am b/plugins/webinterface/www/Makefile.am
new file mode 100644
index 0000000..8a92573
--- /dev/null
+++ b/plugins/webinterface/www/Makefile.am
@@ -0,0 +1,5 @@
+METASOURCES = AUTO
+SUBDIRS = default mobile coldmilk
+
+
+
diff --git a/plugins/webinterface/www/coldmilk/Makefile.am b/plugins/webinterface/www/coldmilk/Makefile.am
new file mode 100644
index 0000000..d6a5f08
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+
+ktdatadir = $(kde_datadir)/ktorrent/www/coldmilk
+
+ktdata_DATA = favicon.ico icon.png interface.js interface.php login.html page_update.js rest.php shutdown.php style.css
+
+SUBDIRS= icons
diff --git a/plugins/webinterface/www/coldmilk/favicon.ico b/plugins/webinterface/www/coldmilk/favicon.ico
new file mode 100644
index 0000000..3213b23
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/favicon.ico
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icon.png b/plugins/webinterface/www/coldmilk/icon.png
new file mode 100644
index 0000000..6cb2185
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icon.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/16x16/Makefile.am b/plugins/webinterface/www/coldmilk/icons/16x16/Makefile.am
new file mode 100644
index 0000000..7380c25
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/16x16/Makefile.am
@@ -0,0 +1,6 @@
+METASOURCES = AUTO
+
+ktdatadir = $(kde_datadir)/ktorrent/www/coldmilk/icons/16x16/
+
+ktdata_DATA = edit_user.png high_priority.png low_priority.png normal_priority.png only_seed.png
+
diff --git a/plugins/webinterface/www/coldmilk/icons/16x16/edit_user.png b/plugins/webinterface/www/coldmilk/icons/16x16/edit_user.png
new file mode 100644
index 0000000..9e5173f
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/16x16/edit_user.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/16x16/high_priority.png b/plugins/webinterface/www/coldmilk/icons/16x16/high_priority.png
new file mode 100644
index 0000000..bcde52b
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/16x16/high_priority.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/16x16/low_priority.png b/plugins/webinterface/www/coldmilk/icons/16x16/low_priority.png
new file mode 100644
index 0000000..966e22b
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/16x16/low_priority.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/16x16/normal_priority.png b/plugins/webinterface/www/coldmilk/icons/16x16/normal_priority.png
new file mode 100644
index 0000000..d39228b
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/16x16/normal_priority.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/16x16/only_seed.png b/plugins/webinterface/www/coldmilk/icons/16x16/only_seed.png
new file mode 100644
index 0000000..254d74a
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/16x16/only_seed.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/22x22/Makefile.am b/plugins/webinterface/www/coldmilk/icons/22x22/Makefile.am
new file mode 100644
index 0000000..2bd411f
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/22x22/Makefile.am
@@ -0,0 +1,6 @@
+METASOURCES = AUTO
+
+ktdatadir = $(kde_datadir)/ktorrent/www/coldmilk/icons/22x22/
+
+ktdata_DATA = exit.png ktstart_all.png ktstop_all.png remove.png start.png stop.png
+
diff --git a/plugins/webinterface/www/coldmilk/icons/22x22/exit.png b/plugins/webinterface/www/coldmilk/icons/22x22/exit.png
new file mode 100644
index 0000000..7ca3753
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/22x22/exit.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/22x22/ktstart_all.png b/plugins/webinterface/www/coldmilk/icons/22x22/ktstart_all.png
new file mode 100644
index 0000000..1c55069
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/22x22/ktstart_all.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/22x22/ktstop_all.png b/plugins/webinterface/www/coldmilk/icons/22x22/ktstop_all.png
new file mode 100644
index 0000000..8086b69
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/22x22/ktstop_all.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/22x22/remove.png b/plugins/webinterface/www/coldmilk/icons/22x22/remove.png
new file mode 100644
index 0000000..3da332f
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/22x22/remove.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/22x22/start.png b/plugins/webinterface/www/coldmilk/icons/22x22/start.png
new file mode 100644
index 0000000..c7995a0
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/22x22/start.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/22x22/stop.png b/plugins/webinterface/www/coldmilk/icons/22x22/stop.png
new file mode 100644
index 0000000..5b59e46
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/22x22/stop.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/32x32/Makefile.am b/plugins/webinterface/www/coldmilk/icons/32x32/Makefile.am
new file mode 100644
index 0000000..a2b93df
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/32x32/Makefile.am
@@ -0,0 +1,6 @@
+METASOURCES = AUTO
+
+ktdatadir = $(kde_datadir)/ktorrent/www/coldmilk/icons/32x32/
+
+ktdata_DATA = configure.png extender_opened.png fileopen.png folder1.png
+
diff --git a/plugins/webinterface/www/coldmilk/icons/32x32/configure.png b/plugins/webinterface/www/coldmilk/icons/32x32/configure.png
new file mode 100644
index 0000000..b45d80a
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/32x32/configure.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/32x32/extender_opened.png b/plugins/webinterface/www/coldmilk/icons/32x32/extender_opened.png
new file mode 100644
index 0000000..b8e652b
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/32x32/extender_opened.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/32x32/fileopen.png b/plugins/webinterface/www/coldmilk/icons/32x32/fileopen.png
new file mode 100644
index 0000000..e4064bb
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/32x32/fileopen.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/32x32/folder1.png b/plugins/webinterface/www/coldmilk/icons/32x32/folder1.png
new file mode 100644
index 0000000..f1a0279
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/32x32/folder1.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/48x48/Makefile.am b/plugins/webinterface/www/coldmilk/icons/48x48/Makefile.am
new file mode 100644
index 0000000..9318a0c
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/48x48/Makefile.am
@@ -0,0 +1,6 @@
+METASOURCES = AUTO
+
+ktdatadir = $(kde_datadir)/ktorrent/www/coldmilk/icons/48x48/
+
+ktdata_DATA = exit.png switchuser.png
+
diff --git a/plugins/webinterface/www/coldmilk/icons/48x48/exit.png b/plugins/webinterface/www/coldmilk/icons/48x48/exit.png
new file mode 100644
index 0000000..fd44eb0
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/48x48/exit.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/48x48/switchuser.png b/plugins/webinterface/www/coldmilk/icons/48x48/switchuser.png
new file mode 100644
index 0000000..e85c801
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/48x48/switchuser.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/64x64/Makefile.am b/plugins/webinterface/www/coldmilk/icons/64x64/Makefile.am
new file mode 100644
index 0000000..3ff3ae0
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/64x64/Makefile.am
@@ -0,0 +1,6 @@
+METASOURCES = AUTO
+
+ktdatadir = $(kde_datadir)/ktorrent/www/coldmilk/icons/64x64/
+
+ktdata_DATA = down.png folder1_man.png looknfeel.png
+
diff --git a/plugins/webinterface/www/coldmilk/icons/64x64/down.png b/plugins/webinterface/www/coldmilk/icons/64x64/down.png
new file mode 100644
index 0000000..a855ecc
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/64x64/down.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/64x64/folder1_man.png b/plugins/webinterface/www/coldmilk/icons/64x64/folder1_man.png
new file mode 100644
index 0000000..a0951d6
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/64x64/folder1_man.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/64x64/looknfeel.png b/plugins/webinterface/www/coldmilk/icons/64x64/looknfeel.png
new file mode 100644
index 0000000..2d2bba2
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/64x64/looknfeel.png
Binary files differ
diff --git a/plugins/webinterface/www/coldmilk/icons/Makefile.am b/plugins/webinterface/www/coldmilk/icons/Makefile.am
new file mode 100644
index 0000000..cbe7db5
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/icons/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS= 16x16 22x22 32x32 48x48 64x64
diff --git a/plugins/webinterface/www/coldmilk/interface.js b/plugins/webinterface/www/coldmilk/interface.js
new file mode 100644
index 0000000..2f361a5
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/interface.js
@@ -0,0 +1,44 @@
+ function validate(action)
+ {
+ var msg;
+ if (action == "remove_torrent") {
+ msg = "Are you sure that you want remove this torrent?";
+ }
+ else if (action == "quit_program") {
+ msg = "Are you sure you want to quit ktorrent?";
+ }
+ else {
+ msg = "Do it?";
+ }
+ return confirm(msg);
+ };
+
+ function show(id)
+ {
+ var items = new Array();
+ items[0] = "torrent_list";
+ items[1] = "torrents_details";
+ items[2] = "preferences";
+ items[3] = "torrent_add";
+ items[4] = "action";
+
+ hide_divs(items);
+
+
+ // Show selected
+ var item_show = document.getElementById(id);
+ item_show.style.display = "";
+
+ };
+
+ function hide_divs(items) {
+ for (var i in items) {
+ var item_hide = document.getElementById(items[i]);
+ if (item_hide != null && !item_hide.style.display) { // not means it's showing..
+ item_hide.style.display = "none";
+ }
+ }
+ return true;
+ }
+
+
diff --git a/plugins/webinterface/www/coldmilk/interface.php b/plugins/webinterface/www/coldmilk/interface.php
new file mode 100644
index 0000000..246f347
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/interface.php
@@ -0,0 +1,251 @@
+<?php
+ $refresh_rate = 5;
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+ <title>ktorrent web interface</title>
+ <link rel="stylesheet" href="style.css" type="text/css" />
+ <meta name="GENERATOR" content="Quanta Plus" />
+ <meta http-equiv="Content-Type" content="text/html"/>
+ <script type="text/javascript" src="page_update.js"></script>
+ <script type="text/javascript" src="interface.js"></script>
+
+</head>
+<body onload="update_interval(<?php echo $refresh_rate; ?>);">
+
+
+<div id="header">
+ <div id="logout">
+ <img src="icons/16x16/edit_user.png" alt="logout" /> <a href="login.html">Sign out</a>
+ </div>
+
+ <a href="interface.php">
+ <img src="icon.png" alt="reload" title="reload"
+ id="header_icon" />
+ </a>
+
+ <ul>
+ <li>
+ <img src="icons/32x32/folder1.png" alt="icon" />
+ <a href="javascript:show('torrent_list');">Torrents</a>
+ </li>
+ <li>
+ <img src="icons/32x32/configure.png" alt="icon" />
+ <a href="javascript:show('preferences');">Preferences</a>
+ </li>
+
+ <li>
+ <img src="icons/32x32/fileopen.png" alt="icon" />
+ <a href="javascript:show('torrent_add');">Add torrent</a>
+ </li>
+ <li>
+ <img src="icons/32x32/extender_opened.png" alt="exit" />
+ <a href="javascript:show('action');">Action</a>
+ </li>
+ </ul>
+
+ <div id="status_bar">
+ <table id="status_bar_table"><tr><td></td></tr></table>
+ </div>
+</div>
+
+
+<!-- Torrents -->
+<div id="torrent_list">
+ <table id="torrent_list_table" class="list_table">
+ <tr><td></td></tr><!--let's be XHTML valid-->
+ </table>
+
+ <div id="bottom-menu">
+ <ul>
+ <li>
+ <img src="icons/22x22/ktstart_all.png" alt="" />
+ <span>
+ <a href="interface.php?startall=true">Start all</a>
+ </span>
+ </li>
+ <li>
+ <img src="icons/22x22/ktstop_all.png" alt="" />
+ <span>
+ <a href="interface.php?stopall=true">Stop all</a>
+ </span>
+ </li>
+ </ul>
+
+ </div>
+
+</div>
+<!-- end torrents -->
+
+
+<!-- Torrent's details -->
+<div id="torrents_details" style="display : none;">
+ <table id="torrents_details_files" class="list_table">
+ <tr><td></td></tr><!--let's be XHTML valid-->
+ </table>
+</div>
+<!-- end torrent's details -->
+
+
+<!-- Preferences -->
+<div id="preferences" style="display : none;">
+ <h2>Preferences</h2>
+ <form action="interface.php" method="get">
+ <div class="simple_form">
+ <img src="icons/64x64/down.png" alt="" />
+
+ <h2>Downloads</h2>
+
+ <?php $globalinfo = globalinfo() ?>
+ <div class="item" style="margin-top : 0em;">
+ Upload speed:
+ <div class="option">
+ <input type="text" name="maximum_upload_rate"
+ value="<?php echo $globalinfo['max_upload_speed']; ?>" />
+ </div>
+ </div>
+
+ <div class="item">
+ Download speed:
+ <div class="option">
+ <input type="text" name="maximum_download_rate"
+ value="<?php echo $globalinfo['max_download_speed']; ?>" />
+ </div>
+ </div>
+
+ <div class="item">
+ Max downloads:
+ <div class="option">
+ <input type="text" name="maximum_downloads"
+ value="<?php echo $globalinfo['max_downloads']; ?>" />
+ </div>
+ </div>
+
+ <div class="item">
+ Max seeds
+ <div class="option">
+ <div style="display : inline;">
+ <input type="text" name="maximum_seeds"
+ value="<?php echo $globalinfo['max_seeds']; ?>" />
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="simple_form" style="margin-top : 1em;" >
+ <img src="icons/64x64/looknfeel.png" alt="" />
+
+ <h2>Web interface</h2>
+
+ <div class="hints">
+ Note: Disabled for now. If you insist, change $refresh_rate in the file interface.php
+ </div>
+
+ <div class="item">
+ <?php
+ $refresh_options = array(
+ '2' => '2 seconds',
+ '3' => '3 seconds',
+ '5' => '5 seconds',
+ '10' => '10 seconds',
+ '30' => '30 seconds',
+ '0' => 'never'
+ );
+ echo 'Update rate:';
+ echo '<div class="option">';
+ echo '<select name="refresh_rate" disabled="disabled">';
+ foreach(array_keys($refresh_options) as $value) {
+ echo '<option value="'.$value.'"';
+ if ($refresh_rate == $value) {
+ echo ' selected="selected"';
+ }
+ echo '>'.$refresh_options[$value].'</option>';
+ }
+ echo '</select>';
+ echo '</div>';
+ ?>
+ </div>
+
+ </div>
+
+ <div style="margin-top : 1em; float : left; clear : both;">
+ <input type="submit" value="Submit preferences" class="buttons"/>
+ </div>
+ </form>
+</div>
+<!-- end preferences -->
+
+
+<!-- Add Torrent -->
+<div id="torrent_add" style="display : none;">
+ <h2>Add a torrent</h2>
+
+ <div class="simple_form">
+ <img src="icons/64x64/folder1_man.png" alt="" />
+ <h3>Load a torrent</h3>
+
+ <form action="interface.php" method="get">
+ <div class="item">
+ URL:
+ <div class="option">
+ <input type="text" name="load_torrent" style="width : 240px;" />
+ <br /><span>Example: http://ktorrent.org/down/latest.torrent</span>
+
+ <div style="margin-top : 1em;">
+ <input type="submit" value="Load Torrent" />
+ </div>
+ </div>
+ </div>
+ </form>
+
+ <h3 style="margin-top : 6em;">Upload a torrent</h3>
+
+ <form action="interface.php" method="post" enctype="multipart/form-data">
+ <div class="item" style="min-height : 5em;">
+ File path:
+ <div class="option">
+ <div style="display : inline;">
+ <input type="file" name="load_torrent" style="width:240px;" />
+ </div>
+
+ <div style="margin-top : 1em;">
+ <input type="submit" name="Upload Torrent" value="Upload Torrent" />
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+</div>
+<!-- end add torrent -->
+
+
+<!-- Action -->
+<div id="action" style="display : none;">
+ <h2 style="margin-top : 0; padding-top : 0;">Actions</h2>
+ <ul>
+ <li>
+ <img src="icons/48x48/switchuser.png" alt="sign out" />
+ <span style="margin-left : 52px;">
+ <a href="login.html">Sign out</a></span>
+
+
+ </li>
+
+ <li>
+ <img src="icons/48x48/exit.png" alt="quit" />
+ <span style="margin-left : 52px;">
+ <a href="shutdown.php?quit=quit" onclick="return validate('quit_program')">
+ Quit program
+ </a></span>
+ </li>
+
+ </ul>
+
+</div>
+<!-- end action -->
+
+
+</body>
+</html>
diff --git a/plugins/webinterface/www/coldmilk/login.html b/plugins/webinterface/www/coldmilk/login.html
new file mode 100644
index 0000000..fa95faf
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/login.html
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+ <title>ktorrent - web interface</title>
+ <link rel="stylesheet" href="style.css" type="text/css" />
+ <meta name="GENERATOR" content="Quanta Plus" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+ <script type="text/javascript">
+ function focus_login() {
+ document.forms["login_form"].elements["username"].focus();
+ }
+ </script>
+</head>
+<body onload="focus_login();">
+
+
+
+<form action="interface.php" method="post" id="login_form">
+<div class="simple_form">
+
+ <img src="icon.png" alt="logo"
+ id="icon_right" />
+
+ <h2>Log in</h2>
+
+
+ <div class="item" style="margin-top : 0em;">
+ Username:
+ <div class="option">
+ <input type="text" name="username" />
+ </div>
+ </div>
+
+ <div class="item" style="min-height : 6em;">
+ Password:
+ <div class="option">
+ <div style="display : inline;">
+ <input type="password" name="password" />
+ </div>
+
+ <div style="margin-top : 1em;">
+ <input type="submit" name="Login" value="Sign in" />
+ </div>
+ </div>
+ </div>
+
+
+
+</div>
+</form>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/webinterface/www/coldmilk/page_update.js b/plugins/webinterface/www/coldmilk/page_update.js
new file mode 100644
index 0000000..c004456
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/page_update.js
@@ -0,0 +1,429 @@
+ /***************************************************************************
+ * Copyright (C) 2007 by Dagur Valberg Johannsson *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+var details_of_torrent = null; //id of torrent which details are displayed
+
+function update_interval(time) {
+ update_all();
+ if (!time) {
+ return;
+ }
+ var seconds = time * 1000;
+ window.setInterval(update_all, seconds);
+}
+
+function update_all() {
+ fetch_xml("rest.php?global_status", new Array("update_status_bar", "update_title"));
+ fetch_xml("rest.php?download_status", new Array("update_torrent_table"));
+}
+
+function fetch_xml(url, callback_functions) {
+ var request = false;
+
+ if (window.XMLHttpRequest) { // most browsers
+ request = new XMLHttpRequest();
+// if (request.overrideMimeType) {
+// request.overrideMimeType('text/xml');
+// }
+ }
+
+ else if (window.ActiveXObject) { //ie
+ try {
+ request = new ActiveXObject("Msxml2.XMLHTTP");
+ }
+ catch(e) {
+ try { request = new ActiveXObject("Microsoft.XMLHTTP"); }
+ catch(e) { }
+ }
+ }
+
+ if (!request) {
+ // Browser doesn't support XMLHttpRequest
+ return false;
+ }
+ request.onreadystatechange = function() {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+ //overrideMimeType didn't work in Konqueror,
+ //so we'll have to parse the response into XML
+ //object ourselfs. responseXML won't work.
+ var xmlstring = request.responseText;
+ var xmldoc;
+ if (window.DOMParser) {
+ xmldoc = (new DOMParser())
+ .parseFromString(xmlstring, "text/xml");
+ }
+ else if (window.ActiveXObject) { //ie
+ xmldoc = new ActiveXObject("Microsoft.XMLDOM");
+ xmldoc.async = false;
+ xmldoc.loadXML(xmlstring);
+ }
+
+ for (var i in callback_functions) {
+ eval(callback_functions[i] + "(xmldoc)");
+ }
+
+ }
+ else {
+ // could not fetch
+ }
+ }
+ }
+
+ request.open('GET', url, true);
+ request.send(null);
+}
+
+function update_title(xmldoc) {
+ var down = _get_text(xmldoc, 'download_speed').data;
+ var up = _get_text(xmldoc, 'upload_speed').data;
+ var new_title = "(D: " + down + ") (U: " + up + ") - ktorrent web interface";
+ document.title = new_title;
+}
+
+function update_status_bar(xmldoc) {
+ var newtable = document.createElement('table');
+ newtable.setAttribute('id', 'status_bar_table');
+
+
+ //dht and encryption
+ {
+ var row = newtable.insertRow(0);
+ var cell = row.insertCell(0);
+ var dht = _get_text_from_attribute(xmldoc, 'dht', 'status').data;
+ var encryption = _get_text_from_attribute(xmldoc, 'encryption', 'status').data;
+ cell.appendChild(
+ document.createTextNode("DHT : " +dht));
+ cell = row.insertCell(1);
+ cell.appendChild(
+ document.createTextNode("Encryption : " + encryption));
+ }
+ //speed down/up
+ {
+ var row = newtable.insertRow(1);
+ var cell = row.insertCell(0);
+ cell.appendChild(
+ document.createTextNode("Speed"));
+
+ cell = row.insertCell(1);
+ var down = _get_text(xmldoc, 'download_speed').data;
+ var up = _get_text(xmldoc, 'upload_speed').data;
+ cell.appendChild(
+ document.createTextNode("down: " + down + " / up: " + up));
+ }
+ //transferred
+ {
+ var row = newtable.insertRow(2);
+ var cell = row.insertCell(0);
+ cell.appendChild(
+ document.createTextNode("Transferred"));
+
+ cell = row.insertCell(1);
+ var down = _get_text(xmldoc, 'downloaded_total').data;
+ var up = _get_text(xmldoc, 'uploaded_total').data;
+ cell.appendChild(
+ document.createTextNode("down: " + down + " / up: " + up));
+ }
+ var oldtable = document.getElementById('status_bar_table');
+ oldtable.parentNode.replaceChild(newtable, oldtable);
+}
+
+function update_torrent_table(xmldoc) {
+
+ var newtable = document.createElement('table');
+ newtable.setAttribute('id', 'torrent_list_table');
+ newtable.className='list_table';
+
+ var torrents = xmldoc.getElementsByTagName('torrent');
+ var i = 0;
+ while (torrents[i]) {
+ _torrent_table_row(torrents[i], newtable, i);
+ i++;
+ }
+ _torrent_table_header(newtable.insertRow(0));
+
+ var oldtable = document.getElementById('torrent_list_table');
+ oldtable.parentNode.replaceChild(newtable, oldtable);
+}
+
+function _torrent_table_row(torrent, table, i) {
+ var row = table.insertRow(i);
+ var row_color = (i % 2) ?
+ "#ffffff" : "#dce4f9";
+ row.setAttribute("style", "background-color : " + row_color);
+
+ //actions
+ {
+ var cell = row.insertCell(0);
+ var can_start = (_get_text(torrent, 'running').data) ? 0 : 1; //if torrent is running we can't start it
+ var can_stop = (can_start==1) ? 0 : 1; //opposite of can_start
+ var start_button = _create_action_button('Start', 'start.png', (can_start==1) ? 'start='+i : '');
+ var stop_button = _create_action_button('Stop', 'stop.png', (can_stop==1) ? 'stop='+i : '');
+ var remove_button = _create_action_button('Remove', 'remove.png', 'remove='+i);
+ remove_button.setAttribute("onclick", "return validate('remove_torrent')");
+
+ cell.appendChild(start_button);
+ cell.appendChild(stop_button);
+ cell.appendChild(remove_button);
+ }
+
+ //file
+ {
+ var cell = row.insertCell(1);
+ var file = document.createElement('a');
+ file.setAttribute('href', '#');
+ file.appendChild(_get_text(torrent, 'name'));
+ file.onclick = function()
+ {
+ show('torrents_details');
+ fetch_xml("rest.php?torrents_details="+i, new Array("get_torrents_details"));
+ details_of_torrent = i;
+ };
+ cell.appendChild(file);
+ }
+
+ //status
+ {
+ var cell = row.insertCell(2);
+ cell.appendChild(
+ _get_text(torrent, 'status'));
+ }
+
+ //speed
+ {
+ var cell = row.insertCell(3);
+
+ cell.appendChild(
+ _get_text(torrent, 'download_rate'));
+ cell.appendChild(document.createElement('br'));
+ cell.appendChild(
+ _get_text(torrent, 'upload_rate'));
+ }
+
+ //size
+ {
+ var cell = row.insertCell(4);
+ cell.appendChild(
+ _get_text(torrent, 'size'));
+ }
+
+ //peers
+ {
+ var cell = row.insertCell(5);
+ cell.appendChild(
+ _get_text(torrent, 'peers'));
+ }
+
+ //transferred
+ {
+ var cell = row.insertCell(6);
+
+ cell.appendChild(
+ _get_text(torrent, 'downloaded'));
+ cell.appendChild(document.createElement('br'));
+ cell.appendChild(
+ _get_text(torrent, 'uploaded'));
+ }
+
+ //done
+ {
+ var cell = row.insertCell(7);
+ cell.setAttribute("style", "padding-right : 2px;");
+
+ var percent_done
+ = _get_text_from_attribute(torrent, 'downloaded', 'percent').data;
+
+ var bar = document.createElement('div');
+ bar.setAttribute("class", "percent_bar");
+ bar.setAttribute("style", "width : " + percent_done + "%;");
+ cell.appendChild(bar);
+
+ var bar_text = document.createElement('div');
+ bar_text.appendChild(
+ document.createTextNode(percent_done + "%"));
+
+ bar.appendChild(bar_text);
+ }
+}
+
+//function called after changing file priority to refresh list of files (and priorities)
+function just_refresh_details(xmldoc) {
+ if (details_of_torrent!=null)
+ fetch_xml("rest.php?torrents_details="+details_of_torrent, new Array("get_torrents_details"));
+}
+
+function get_torrents_details(xmldoc) {
+ var newtable = document.createElement('table');
+ newtable.setAttribute('id', 'torrents_details_files');
+ newtable.className='list_table';
+
+ var id = xmldoc.getElementsByTagName('torrents_details')[0].getAttribute('id');
+ var files = xmldoc.getElementsByTagName('file');
+ for(var i=0; i<files.length; i++)
+ {
+ var row = newtable.insertRow(i);
+ row.style.backgroundColor=(i % 2) ? '#ffffff' : '#dce4f9';
+ var cell = row.insertCell(-1);
+
+ var file_status = _get_text(files[i], 'status').data;
+ var command; //we call ?file_xx - this call is detected by server and priority is being changed
+
+ command = (file_status==50)?'':'rest.php?file_hp='+id+'-'+i;
+ var high_prior = _create_file_action_button('/icons/16x16/high_priority.png', 'High Priority', command);
+ cell.appendChild(high_prior);
+
+ command = (file_status==40)?'':'rest.php?file_np='+id+'-'+i;
+ var normal_prior = _create_file_action_button('/icons/16x16/normal_priority.png', 'Normal Priority', command);
+ cell.appendChild(normal_prior);
+
+ command = (file_status==30)?'':'rest.php?file_lp='+id+'-'+i;
+ var low_prior = _create_file_action_button('/icons/16x16/low_priority.png', 'Low Priority', command);
+ cell.appendChild(low_prior);
+
+ command = (file_status==20 || file_status==10)?'':'rest.php?file_stop='+id+'-'+i;
+ var dnd = _create_file_action_button('/icons/16x16/only_seed.png', 'Stop downloading (Only Seed Priority)', command);
+ cell.appendChild(dnd);
+
+ var cell = row.insertCell(-1);
+ cell.appendChild(_get_text(files[i], 'name'));
+ var cell = row.insertCell(-1);
+ cell.appendChild(_get_text(files[i], 'size'));
+ var cell = row.insertCell(-1);
+
+ if (_get_text(files[i], 'perc_done').data!='')
+ cell.appendChild(_get_text(files[i], 'perc_done'));
+ else
+ cell.appendChild(document.createTextNode("0"));
+ cell.appendChild(document.createTextNode("%"));
+ var cell = row.insertCell(-1);
+
+ cell.appendChild(document.createTextNode(_get_file_status_name(file_status)));
+ }
+
+ _torrents_details_header(newtable.insertRow(0));
+
+ /*var torrents = xmldoc.getElementsByTagName('torrent');
+ var i = 0;
+ while (torrents[i]) {
+ _torrent_table_row(torrents[i], newtable, i);
+ i++;
+ }
+ _torrent_table_header(newtable.insertRow(0));*/
+
+ var oldtable = document.getElementById('torrents_details_files');
+ oldtable.parentNode.replaceChild(newtable, oldtable);
+}
+
+function _create_action_button(button_name, image_src, command) {
+ var image = document.createElement("img");
+ image.setAttribute("src", "icons/22x22/" + image_src);
+ image.setAttribute("alt", button_name);
+ image.setAttribute("title", button_name);
+ if (command != '')
+ {
+ var a = document.createElement("a");
+ a.setAttribute("href", "interface.php?" + command);
+ a.appendChild(image);
+ return a;
+ }
+ else
+ return image;
+}
+
+function _create_file_action_button(img_src, img_alt, command) {
+ var image = document.createElement("img");
+ image.setAttribute("src", img_src);
+ image.setAttribute("alt", img_alt);
+ image.setAttribute("title", img_alt);
+ if (command != '')
+ {
+ var a = document.createElement("a");
+ a.setAttribute("href", "#");
+ a.onclick = function()
+ {
+ fetch_xml(command, new Array("just_refresh_details"));
+ };
+ a.appendChild(image);
+ return a;
+ }
+ else
+ return image;
+}
+
+// gets element with given tag and crates text node from it
+function _get_text(element, tag) {
+ var text_node;
+ try {
+ text_node = document.createTextNode(
+ element.getElementsByTagName(tag)[0].firstChild.data);
+ }
+ catch (e) {
+ text_node = document.createTextNode('');
+ }
+ return text_node;
+}
+
+function _get_text_from_attribute(element, tag, attribute) {
+ var text_node;
+ try {
+ text_node = document.createTextNode(
+ element.getElementsByTagName(tag)[0].getAttribute(attribute));
+ }
+ catch (e) {
+ text_node = document.createTextNode('');
+ }
+ return text_node;
+}
+
+function _get_file_status_name(status_id)
+{
+ if (status_id==60) return 'PREVIEW_PRIORITY';
+ else if (status_id==50) return 'Download First';
+ else if (status_id==40) return 'Download Normally';
+ else if (status_id==30) return 'Download Last';
+ else if (status_id==20) return 'Only Seed';
+ else if (status_id==10) return 'Do Not Download';
+ else return 'Not supported file status';
+}
+
+function _torrents_details_header(row) {
+ headers = new Array("Actions", "File", "Size", "Perc done", "Status");
+ for (var i in headers) {
+ var header = document.createElement("th");
+ header.appendChild(document.createTextNode(headers[i]));
+ row.appendChild(header);
+ }
+ return row;
+}
+
+function _torrent_table_header(row) {
+ headers = new Array(
+ "Actions", "File", "Status",
+ "Speed", "Size", "Peers",
+ "Transferred", "% done"
+ );
+
+ for (var i in headers) {
+ var header = document.createElement("th");
+ header.appendChild(
+ document.createTextNode(headers[i]));
+ row.appendChild(header);
+ }
+ return row;
+}
diff --git a/plugins/webinterface/www/coldmilk/rest.php b/plugins/webinterface/www/coldmilk/rest.php
new file mode 100644
index 0000000..bab7e68
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/rest.php
@@ -0,0 +1,265 @@
+<?php
+
+ /***************************************************************************
+ * Copyright (C) 2007 by Dagur Valberg Johannsson *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+/**
+ * Simple REST interface.
+ */
+
+$rest_commands = array(
+ "global_status",
+ "download_status",
+ "torrents_details"
+);
+
+if (!array_keys($_REQUEST)) {
+ header("Content-Type: text/html");
+ print "<html><body>
+ Usage: rest.php?command<br />
+ Available commands: ";
+ foreach ($rest_commands as $command)
+ print "$command ";
+
+ print "</body></html>";
+}
+
+else {
+ header("Content-Type: text/xml");
+ $rest = new RestInterface();
+ foreach($_REQUEST as $command=>$arg)
+ {
+ if (in_array($command, $rest_commands))
+ print $rest->$command($arg);
+ else
+ print "Unknown command " . htmlentities($command) . "<br />";
+
+ }
+}
+
+// classes
+
+class RestInterface {
+ public function global_status() {
+ $info = globalinfo();
+ $common = new Common();
+
+ $down_speed = $info['download_speed'];
+ $up_speed = $info['upload_speed'];
+
+ $down_total
+ = $common->bytes_to_readable($info['bytes_downloaded']);
+ $up_total
+ = $common->bytes_to_readable($info['bytes_uploaded']);
+
+ $dht = $info['dht_support'] ? "on" : "off";
+ $encryption = $info['use_encryption'] ? "on" : "off";
+
+ $xml = new KTorrentXML('status_bar');
+
+ $elements = array(
+ $xml->new_element('download_speed' , $down_speed),
+ $xml->new_element('upload_speed' , $up_speed),
+ $xml->new_element('downloaded_total', $down_total),
+ $xml->new_element('uploaded_total', $up_total),
+
+ $xml->new_element('dht', null, array('status' => $dht)),
+ $xml->new_element('encryption', null, array('status' => $encryption)),
+ );
+
+ foreach($elements as $element) {
+ $xml->append_to_root($element);
+ }
+
+ return $xml->saveXML();
+
+ }
+
+ public function download_status() {
+ $download_status = downloadstatus();
+ $xml = new KTorrentXML('download_status');
+ foreach($download_status as $torrent) {
+ $torrent_xml = $xml->new_element('torrent');
+ $xml->append_to_root($torrent_xml);
+
+// foreach(array_keys($torrent) as $key) {
+// $torrent_xml->appendChild(
+// $xml->new_element("raw_$key", $torrent[$key]));
+// }
+
+ $status = $torrent['status'];
+ $done = $torrent['bytes_downloaded'];
+ $total_bytes = $torrent['total_bytes_to_download'];
+ $bytes_left = $torrent['bytes_left_to_download'];
+ $elements = array(
+
+ $xml->new_element('name',
+ $this->_clean_name($torrent['torrent_name'])),
+
+ $xml->new_element('status',
+ $this->_torrent_status($status), array('id' => $status)),
+
+ $xml->new_element('running', $torrent['running']),
+ $xml->new_element('download_rate', $torrent['download_rate']." down"),
+ $xml->new_element('upload_rate', $torrent['upload_rate']." up"),
+ $xml->new_element('size', $torrent['total_bytes']),
+ $xml->new_element('peers', $torrent['num_peers']),
+ $xml->new_element('uploaded', $torrent['bytes_uploaded']." uploaded"),
+
+
+ $xml->new_element('downloaded',
+ "$done downloaded",
+ array('percent' => $this->_get_percent_done($total_bytes, $bytes_left))),
+ );
+
+ foreach($elements as $element) {
+ $torrent_xml->appendChild($element);
+ }
+ }
+
+ return $xml->saveXML();
+
+ }
+
+ public function torrents_details($torrent_id) {
+ $xml = new KTorrentXML('torrents_details', null, array('id'=>$torrent_id));
+ $download_status = downloadstatus();
+ if (isset($download_status[$torrent_id]))
+ foreach($download_status[$torrent_id]['files'] as $id=>$info)
+ {
+ $file_xml = $xml->new_element('file', '', array('id'=>$id));
+ $xml->append_to_root($file_xml);
+ foreach($info as $key=>$val)
+ $file_xml->appendChild($xml->new_element($key, $val));
+ }
+ return $xml->saveXML();
+ }
+
+ // Helper function for download_status
+ private function _torrent_status($status_id) {
+ $status = array(
+ 0 => "Not started",
+ 1 => "Seeding Complete",
+ 2 => "Download Complete",
+ 3 => "Seeding",
+ 4 => "Downloading",
+ 5 => "Stalled",
+ 6 => "Stopped",
+ 7 => "Allocating Diskspace",
+ 8 => "Error",
+ 9 => "Queued",
+ 10 => "Checking Data"
+ );
+
+ return $status[$status_id];
+ }
+
+
+ // Truncate long torrent name, and HTML escape it.
+ // This is a helper function for download_status.
+ private function _clean_name($name) {
+ $name = str_replace("'", "\'", $name);
+ if (strlen($name) > 30) {
+ $name = substr($name, 0, 27);
+ $name .= "...";
+ }
+ $name = htmlspecialchars($name);
+ return $name;
+ }
+
+ // Calculate percent done.
+ // Helper function for download_status
+ private function _get_percent_done($bytes_total, $bytes_left) {
+ if($bytes_total) {
+ $perc_done = round(100.0 - ($bytes_left / $bytes_total) * 100);
+ return $perc_done;
+ }
+ else {
+ return 0;
+ }
+ }
+}
+
+
+/**
+ * Class to build a xml tree
+ */
+class KTorrentXML extends DomDocument {
+ private $root_element;
+ public function __construct($root, $value = null, $attributes = null) {
+ parent::__construct('1.0');
+ $this->root_element = $this->createElement($root);
+ $this->appendChild($this->root_element);
+ $this->formatOutput = true;
+
+ if ($attributes)
+ foreach($attributes as $key=>$val)
+ $this->root_element->setAttribute($key, $val);
+ }
+
+ // Creates an element, and returns it.
+ public function new_element($name, $value = null, $attributes = null) {
+ $element = $this->createElement($name);
+ if ($value) {
+ $element->appendChild($this->createTextNode($value));
+ }
+ if ($attributes) {
+ foreach(array_keys($attributes) as $key) {
+ $element->setAttribute($key, $attributes[$key]);
+ }
+ }
+ return $element;
+ }
+
+ // Append a given element to the root element of the xml file.
+ public function append_to_root($element) {
+ $this->root_element->appendChild($element);
+ }
+
+}
+
+/**
+ * Generic functions
+ */
+class Common {
+ function bytes_to_readable($bytes) {
+ if ($bytes < 1024) {
+ return round($bytes, 2) . " bytes";
+ }
+
+ else if (($kb = ($bytes / 1024)) < 1024) {
+ return round($kb, 2) . " KB";
+ }
+
+ else if (($mb = ($kb / 1024)) < 1024) {
+ return round($mb, 2) . " MB";
+ }
+
+ else {
+ $gb = round($mb / 1024, 2);
+ return "$gb GB";
+ }
+ }
+
+ function kb_to_readable($kbytes) {
+ return Common::bytes_to_readable($kbytes * 1024);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/plugins/webinterface/www/coldmilk/shutdown.php b/plugins/webinterface/www/coldmilk/shutdown.php
new file mode 100644
index 0000000..92862c4
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/shutdown.php
@@ -0,0 +1,21 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+ <title>ktorrent - web interface</title>
+ <link rel="stylesheet" href="style.css" type="text/css" />
+ <meta name="GENERATOR" content="Quanta Plus" />
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+</head>
+<body>
+
+
+<div id="action" style="min-height : 130px;">
+ <img src="icon.png" alt="" style="float : left; border : 0px solid;" />
+ <span style="font-size : x-large; margin-left : 50px;">Goodbye!</span>
+</div>
+
+</form>
+</body>
+</html>
diff --git a/plugins/webinterface/www/coldmilk/style.css b/plugins/webinterface/www/coldmilk/style.css
new file mode 100644
index 0000000..69010dc
--- /dev/null
+++ b/plugins/webinterface/www/coldmilk/style.css
@@ -0,0 +1,212 @@
+body {
+ font-family : sans-serif;
+ color : black;
+ background-color : white;
+ }
+
+A:link { color : blue; }
+A:visited { color : blue; }
+A:active { color : blue; }
+
+
+
+#header {
+ min-height : 130px;
+ }
+
+
+#header #logout {
+ position : absolute;
+ top : 1px;
+ right : 0.5em;
+ }
+
+
+#header #header_icon {
+ float : left;
+ margin-left : 1em;
+ border : 0px solid;
+ }
+
+#header ul {
+ display : inline;
+ font-size : large;
+ list-style-type : none;
+}
+
+
+#header li {
+ display : inline;
+ margin-right : 0.5em;
+ }
+
+
+#header #status_bar {
+ font-family : monospace;
+ font-size : small;
+ color : #636363;
+ border : 1px solid;
+ border-color : silver;
+ background-color : white;
+ }
+
+
+.list_table {
+ border-collapse : collapse;
+ width : 100%;
+ }
+
+
+.list_table th {
+ text-align : left;
+ }
+
+
+.list_table img {
+ border : 0px solid;
+ padding : 2px;
+ }
+
+
+.list_table .percent_bar {
+ background-color : #a5d3b4;
+ float : left;
+ position : relative;
+ color : inherit;
+}
+
+.list_table img {
+ opacity: 0.4;
+}
+
+.list_table a img {
+ opacity: 1.0;
+}
+
+#bottom-menu {
+ margin-top : 1em;
+ }
+
+
+#bottom-menu ul {
+ list-style-type : none;
+ padding : 0;
+ margin : 0;
+ }
+
+
+#bottom-menu li {
+ padding : 0.5em;
+ border : 1px solid;
+ border-color : silver;
+ display : inline;
+ position : relative;
+ background-color : #f5f5f5;
+ margin-right : 0.5em;
+ color : blue;
+}
+
+
+#bottom-menu img {
+ position : absolute;
+ top : 0.2em;
+ left : 0.5em;
+ }
+
+
+#bottom-menu span {
+ vertical-align : middle;
+ margin-left : 30px;
+ }
+
+
+.simple_form {
+ border : 1px solid;
+ border-color : silver;
+ width : 60%;
+ float : left;
+ padding : 1em;
+ background-color : #f5f5f5;
+ clear : left;
+ color : black;
+ }
+
+.simple_form h2, .simple_form h3 {
+ margin : 0 0 0.5em 0;
+ padding : 0;
+}
+
+
+.simple_form img {
+ float : right;
+ margin-right : 1em;
+ border : 0px solid;
+ }
+
+
+.simple_form .item {
+ position : relative;
+ margin-top : 0.5em;
+ }
+
+
+.simple_form .item .option {
+ position : absolute;
+ top : 0px;
+ left: 30%;
+ }
+
+
+.simple_form .item .option span {
+ font-size : small;
+ color : #333333;
+ background-color : inherit;
+ }
+
+.simple_form .hints {
+ font-size : small;
+ background-color : #FDFDFD;
+ color : inherit;
+}
+
+#action {
+ border : 1px solid;
+ border-color : silver;
+ clear : both;
+ width : 60%;
+ margin-left : 20%;
+ padding : 1em;
+ background-color : #f5f5f5;
+ color : inherit;
+}
+
+#action ul {
+ list-style-type : none;
+ margin : 0;
+ padding-left : 0;
+ padding-bottom : 1em;
+}
+
+#action li {
+ margin-top : 0.25em;
+ margin-bottom : 0.25em;
+ border : 1px solid;
+ border-color : silver;
+ min-height : 48px;
+ padding : 0.5em;
+ background-color : #FDFDFD;
+ color : inherit;
+
+}
+
+#action li span {
+ font-size : x-large;
+ vertical-align : middle;
+ padding-top : 20px;
+}
+
+#action img {
+ float : left;
+ clear : both;
+ border : 0px solid;
+} \ No newline at end of file
diff --git a/plugins/webinterface/www/default/Makefile.am b/plugins/webinterface/www/default/Makefile.am
new file mode 100644
index 0000000..01f09df
--- /dev/null
+++ b/plugins/webinterface/www/default/Makefile.am
@@ -0,0 +1,9 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+
+ktdatadir = $(kde_datadir)/ktorrent/www/default
+
+ktdata_DATA = details.php only_seed.png favicon.ico grad1.jpg grad2.jpg menu_bg.png \
+ header_tile.png high_priority.png icon.png interface.php ktorrentwebinterfacelogo.png \
+ login.html low_priority.png normal_priority.png remove.png shutdown.php start.png \
+ stop.png style.css stylen.css wz_tooltip.js
diff --git a/plugins/webinterface/www/default/details.php b/plugins/webinterface/www/default/details.php
new file mode 100644
index 0000000..a19d57e
--- /dev/null
+++ b/plugins/webinterface/www/default/details.php
@@ -0,0 +1,89 @@
+<?php
+$stats=downloadStatus();
+$num_torrent=$_REQUEST['torrent'];
+
+function cut_name_if_long($string)
+{
+ if(strlen($string)>30) return substr($string, 0, 30).'...';
+ else return $string;
+}
+
+function get_file_status_name($status_id)
+{
+ $table = array(
+ 60 => 'PREVIEW_PRIORITY',
+ 50 => 'Download First',
+ 40 => 'Download Normally',
+ 30 => 'Download Last',
+ 20 => 'Only Seed',
+ 10 => 'Do Not Download'
+ );
+ if (array_key_exists($status_id, $table)) return $table[$status_id];
+ else return 'Not supported file status';
+}
+
+function generate_file_prior_button_code($img, $alt, $href='')
+{
+ $img = '<img src="'.htmlspecialchars($img).'" alt="'.htmlspecialchars($alt).'" />';
+ if (empty($href)) return $img;
+ else return '<a href="'.htmlspecialchars($href).'">'.$img.'</a>';
+}
+
+$display_name=cut_name_if_long($stats[$num_torrent]['torrent_name']);
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<style type="text/css" media="all">
+ @import "stylen.css";
+</style>
+<meta http-equiv="Content-Type" content="text/html" />
+<link rel="icon" href="favicon.ico" type="image/x-icon" />
+<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
+<title><?php echo 'KTorrent: Details for '.$display_name; ?></title>
+</head>
+<body>
+ <div id="top_bar">WebInterface KTorrent plugin</div>
+ <div id="icon"><img src="icon.png" alt="" /></div>
+ <div id="header">
+ <strong>KTorrent WebInterface</strong>
+ <br />
+ <small>BitTorrent client for KDE</small>
+ </div>
+ <ul id="menu">
+ <li><a href="interface.php" title="BACK">Back</a></li>
+ <li><a href="login.html" title="LOGOUT">Logout</a></li>
+ </ul>
+ <div id="content">
+ <table>
+ <tr>
+ <th>Actions</th>
+ <th>File</th>
+ <th>Status</th>
+ <th>Size</th>
+ <th>Complete</th>
+ </tr>
+<?php
+ foreach($stats[$num_torrent]['files'] as $id => $file)
+ {
+ echo "\t\t".'<tr>'."\n\t\t\t";
+ echo '<td class="actions">';
+ echo generate_file_prior_button_code('/high_priority.png', 'High Priority', $file['status']==50?'':"details.php?file_hp=$num_torrent-$id&torrent=$num_torrent");
+ echo generate_file_prior_button_code('/normal_priority.png', 'Normal Priority', $file['status']==40?'':"details.php?file_np=$num_torrent-$id&torrent=$num_torrent");
+ echo generate_file_prior_button_code('/low_priority.png', 'Low Priority', $file['status']==30?'':"details.php?file_lp=$num_torrent-$id&torrent=$num_torrent");
+ echo generate_file_prior_button_code('/only_seed.png', 'Stop downloading (Only Seed Priority)', ($file['status']==20||$file['status']==10)?'':"details.php?file_stop=$num_torrent-$id&torrent=$num_torrent");
+ echo '</td>';
+ echo '<td>'.htmlspecialchars(cut_name_if_long($file['name'])).'</td>';
+ echo '<td>'.get_file_status_name($file['status']).'</td>';
+ echo '<td style="text-align:right;">'.$file['size'].'</td>';
+ echo '<td style="text-align:right;">'.round($file['perc_done'], 2).' %</td>';
+ echo "\n\t\t".'</tr>'."\n";
+ }
+ ?>
+ </table>
+ </div>
+ <div id="footer">&#169; 2006 WebInterface KTorrent plugin</div>
+</body>
+</html>
diff --git a/plugins/webinterface/www/default/favicon.ico b/plugins/webinterface/www/default/favicon.ico
new file mode 100644
index 0000000..3213b23
--- /dev/null
+++ b/plugins/webinterface/www/default/favicon.ico
Binary files differ
diff --git a/plugins/webinterface/www/default/grad1.jpg b/plugins/webinterface/www/default/grad1.jpg
new file mode 100644
index 0000000..762baa0
--- /dev/null
+++ b/plugins/webinterface/www/default/grad1.jpg
Binary files differ
diff --git a/plugins/webinterface/www/default/grad2.jpg b/plugins/webinterface/www/default/grad2.jpg
new file mode 100644
index 0000000..bc4d77e
--- /dev/null
+++ b/plugins/webinterface/www/default/grad2.jpg
Binary files differ
diff --git a/plugins/webinterface/www/default/header_tile.png b/plugins/webinterface/www/default/header_tile.png
new file mode 100644
index 0000000..0bad736
--- /dev/null
+++ b/plugins/webinterface/www/default/header_tile.png
Binary files differ
diff --git a/plugins/webinterface/www/default/high_priority.png b/plugins/webinterface/www/default/high_priority.png
new file mode 100644
index 0000000..bcde52b
--- /dev/null
+++ b/plugins/webinterface/www/default/high_priority.png
Binary files differ
diff --git a/plugins/webinterface/www/default/icon.png b/plugins/webinterface/www/default/icon.png
new file mode 100644
index 0000000..6cb2185
--- /dev/null
+++ b/plugins/webinterface/www/default/icon.png
Binary files differ
diff --git a/plugins/webinterface/www/default/interface.php b/plugins/webinterface/www/default/interface.php
new file mode 100644
index 0000000..6432798
--- /dev/null
+++ b/plugins/webinterface/www/default/interface.php
@@ -0,0 +1,152 @@
+<?php
+$globalinfo=globalInfo();
+$stats=downloadStatus();
+
+function get_torrent_status_name($status_id)
+{
+ $table = array(
+ 0 => 'Not Started',
+ 1 => 'Seeding Complete',
+ 2 => 'Download Complete',
+ 3 => 'Seeding',
+ 4 => 'Downloading',
+ 5 => 'Stalled',
+ 6 => 'Stopped',
+ 7 => 'Allocating Diskspace',
+ 8 => 'Error',
+ 9 => 'Queued',
+ 10 => 'Checking Data'
+ );
+ if (array_key_exists($status_id, $table)) return $table[$status_id];
+ else return 'Not supported Status';
+}
+
+function generate_button_code($img, $alt, $href='')
+{
+ $img = '<img src="'.htmlspecialchars($img).'" alt="'.htmlspecialchars($alt).'" />';
+ if (empty($href)) return $img;
+ else return '<a href="'.htmlspecialchars($href).'">'.$img.'</a>';
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<style type="text/css" media="all">
+ @import "stylen.css";
+</style>
+<meta http-equiv="Content-Type" content="text/html" />
+<link rel="icon" href="favicon.ico" type="image/x-icon" />
+<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
+<title><?php echo '(D:'.$globalinfo['download_speed'].') (U:'.$globalinfo['upload_speed'].') KTorrent'; ?></title>
+<script type="text/javascript">
+ function validate()
+ {
+ msg = "Are you absolutely sure that you want to remove this torrent?";
+ return confirm(msg);
+ }
+ function validate_shutdown()
+ {
+ msg = "Are you absolutely sure that you want to shutdown KTorrent?";
+ return confirm(msg);
+ }
+</script>
+</head>
+<body>
+ <div id="top_bar">WebInterface KTorrent plugin</div>
+ <div id="icon"><img src="icon.png" alt="" /></div>
+ <div id="header">
+ <strong>KTorrent WebInterface</strong>
+ <br />
+ <small>BitTorrent client for KDE</small>
+ </div>
+ <ul id="menu">
+ <li><a href="shutdown.php?quit=quit" class="shutdown" title="Shutdown KTorrent" onclick="return validate_shutdown()">Shutdown</a></li>
+ <li><a href="interface.php" title="REFRESH">Refresh</a></li>
+ <li><a href="login.html" title="LOGOUT">Logout</a></li>
+ </ul>
+ <div id="sidebar">
+ <div class="box">
+ <h2>Torrent control</h2>
+ <form action="interface.php" method="get" style="text-align: center;">
+ <input type="submit" name="startall" value="Start All" />
+ </form>
+ <hr />
+ <form action="interface.php" method="get" style="text-align: center;">
+ <input type="submit" name="stopall" value="Stop All" />
+ </form>
+ </div>
+ <div class="box">
+ <h2>Settings</h2>
+ <form action="interface.php" method="get">
+ <label>Upload speed <input type="text" name="maximum_upload_rate" value="<?php echo $globalinfo['max_upload_speed']; ?>" class="settingsInput" /></label>
+ <label>Download speed <input type="text" name="maximum_download_rate" value="<?php echo $globalinfo['max_download_speed']; ?>" class="settingsInput" /></label>
+ <label>Maximum downloads <input type="text" name="maximum_downloads" value="<?php echo $globalinfo['max_downloads']; ?>" class="settingsInput" /></label>
+ <label>Maximum seeds <input type="text" name="maximum_seeds" value="<?php echo $globalinfo['max_seeds']; ?>" class="settingsInput" /></label>
+ <input type="submit" value="Submit settings" />
+ </form>
+ </div>
+ <div class="box">
+ <h2>Load torrents</h2>
+ <form action="interface.php" method="get">
+ <label class="wide">Torrent URL: <input type="text" name="load_torrent" /></label>
+ <input type="submit" value="Load Torrent" />
+ </form>
+ <hr />
+ <form method="post" enctype="multipart/form-data" action="interface.php">
+ <label class="wide">Local File:<input type="file" name="load_torrent" /></label>
+ <input type="submit" name="Upload Torrent" value="Upload Torrent" />
+ </form>
+ </div>
+ </div>
+ <div id="content">
+ <table>
+ <tr>
+ <th>Actions</th>
+ <th>File</th>
+ <th>Status</th>
+ <th>Downloaded</th>
+ <th>Size</th>
+ <th>Uploaded</th>
+ <th>Down Speed</th>
+ <th>Up Speed</th>
+ <th>Peers</th>
+ <th>Complete</th>
+ </tr>
+<?php
+ $a = 0;
+ foreach ($stats as $torrent) {
+ echo "\t\t".'<tr>'."\n\t\t\t";
+
+ $torrent_name = str_replace("'", "\'", $torrent['torrent_name']);
+ if($torrent['total_bytes_to_download']!=0) $perc = round(100.0 - ($torrent['bytes_left_to_download'] / $torrent['total_bytes_to_download']) * 100.0, 2);
+ else $perc = 0;
+ if(strlen($torrent['torrent_name'])>30) $display_name=substr($torrent['torrent_name'], 0, 30)." ...";
+ else $display_name=$torrent['torrent_name'];
+ if ($torrent['num_files']>1) $file_td_content = '<a href="details.php?torrent='.$a.'">'.$display_name.'</a>';
+ else $file_td_content = $display_name;
+
+ echo '<td class="actions">';
+ echo generate_button_code('/stop.png', 'stop', ($torrent['running'])?'interface.php?stop='.$a:'');
+ echo generate_button_code('/start.png', 'start', ($torrent['running'])?'':'interface.php?start='.$a);
+ echo '<a href="interface.php?remove='.$a.'" onclick="return validate()"><img src="/remove.png" alt="remove" /></a>';
+ echo '</td>';
+ echo "<td style=\"text-align:left;\" onmouseover=\"this.T_TITLE='$torrent_name';return escape('Download speed:&lt;strong&gt;{$torrent['download_rate']}&lt;/strong&gt;&lt;br /&gt; Upload speed:&lt;strong&gt;{$torrent['upload_rate']}&lt;/strong&gt;&lt;/td&gt;')\">$file_td_content</td>";
+ echo '<td>'.get_torrent_status_name($torrent['status']).'</td>';
+ echo '<td style="text-align:right;">'.$torrent['bytes_downloaded'].'</td>';
+ echo '<td style="text-align:right; padding-left:8px;">'.$torrent['total_bytes'].'</td>';
+ echo '<td style="text-align:right; padding-left:8px;">'.$torrent['bytes_uploaded'].'</td>';
+ echo '<td style="text-align:right;">'.$torrent['download_rate'].'</td>';
+ echo '<td style="text-align:right;">'.$torrent['upload_rate'].'</td>';
+ echo '<td>'.$torrent['num_peers'].'</td>';
+ echo '<td style="text-align:right;">'.$perc.'% </td>';
+ echo "\n\t\t".'</tr>'."\n";
+ $a++;
+ }
+ ?>
+ </table>
+ </div>
+ <div id="footer">&#169; 2006 WebInterface KTorrent plugin</div>
+ <script type="text/javascript" src="wz_tooltip.js"></script>
+</body>
+</html>
diff --git a/plugins/webinterface/www/default/ktorrentwebinterfacelogo.png b/plugins/webinterface/www/default/ktorrentwebinterfacelogo.png
new file mode 100644
index 0000000..bc235b5
--- /dev/null
+++ b/plugins/webinterface/www/default/ktorrentwebinterfacelogo.png
Binary files differ
diff --git a/plugins/webinterface/www/default/login.html b/plugins/webinterface/www/default/login.html
new file mode 100644
index 0000000..93ac1e6
--- /dev/null
+++ b/plugins/webinterface/www/default/login.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>KTorrent WebInterface - Login</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<link rel="stylesheet" type="text/css" href="style.css" />
+<script type="text/javascript">
+ var was_focused = false;
+ function try_focus_form()
+ {
+ if (!was_focused)
+ {
+ document.forms["loginForm"].elements["username"].focus();
+ was_focused = true;
+ }
+ }
+</script>
+</head>
+<body onload="try_focus_form();">
+ <form id="loginForm" action="interface.php" method="post">
+ <table style="padding-left:100px; padding-top: 350px;">
+ <tr><td>Username:</td><td><input type="text" name="username" onfocus="was_focused = true;" /></td></tr>
+ <tr><td>Password:</td><td><input type="password" name="password" /></td></tr>
+ <tr><td></td><td><input type="submit" value="Login" /></td></tr>
+ </table>
+ </form>
+</body>
+</html>
diff --git a/plugins/webinterface/www/default/low_priority.png b/plugins/webinterface/www/default/low_priority.png
new file mode 100644
index 0000000..966e22b
--- /dev/null
+++ b/plugins/webinterface/www/default/low_priority.png
Binary files differ
diff --git a/plugins/webinterface/www/default/menu_bg.png b/plugins/webinterface/www/default/menu_bg.png
new file mode 100644
index 0000000..d33169a
--- /dev/null
+++ b/plugins/webinterface/www/default/menu_bg.png
Binary files differ
diff --git a/plugins/webinterface/www/default/normal_priority.png b/plugins/webinterface/www/default/normal_priority.png
new file mode 100644
index 0000000..d39228b
--- /dev/null
+++ b/plugins/webinterface/www/default/normal_priority.png
Binary files differ
diff --git a/plugins/webinterface/www/default/only_seed.png b/plugins/webinterface/www/default/only_seed.png
new file mode 100644
index 0000000..254d74a
--- /dev/null
+++ b/plugins/webinterface/www/default/only_seed.png
Binary files differ
diff --git a/plugins/webinterface/www/default/remove.png b/plugins/webinterface/www/default/remove.png
new file mode 100644
index 0000000..5d40f9f
--- /dev/null
+++ b/plugins/webinterface/www/default/remove.png
Binary files differ
diff --git a/plugins/webinterface/www/default/shutdown.php b/plugins/webinterface/www/default/shutdown.php
new file mode 100644
index 0000000..f87a199
--- /dev/null
+++ b/plugins/webinterface/www/default/shutdown.php
@@ -0,0 +1,13 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Shutdown page</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+</head>
+<body bgcolor="#a2d2b1">
+
+<center><strong>KTorrent closed successfully</strong></center>
+
+</body>
+</html>
diff --git a/plugins/webinterface/www/default/start.png b/plugins/webinterface/www/default/start.png
new file mode 100644
index 0000000..3aac1a5
--- /dev/null
+++ b/plugins/webinterface/www/default/start.png
Binary files differ
diff --git a/plugins/webinterface/www/default/stop.png b/plugins/webinterface/www/default/stop.png
new file mode 100644
index 0000000..530639f
--- /dev/null
+++ b/plugins/webinterface/www/default/stop.png
Binary files differ
diff --git a/plugins/webinterface/www/default/style.css b/plugins/webinterface/www/default/style.css
new file mode 100644
index 0000000..3af4bb5
--- /dev/null
+++ b/plugins/webinterface/www/default/style.css
@@ -0,0 +1,13 @@
+/* styles for login.html */
+
+body {
+ background: url(ktorrentwebinterfacelogo.png) no-repeat 120px 120px;
+ font-family: verdana, arial, sans-serif;
+ color: #000000;
+ font-size: 12px;
+}
+
+input, textarea {
+ border: 1px solid #000000;
+ width: 120px;
+}
diff --git a/plugins/webinterface/www/default/stylen.css b/plugins/webinterface/www/default/stylen.css
new file mode 100644
index 0000000..68966fd
--- /dev/null
+++ b/plugins/webinterface/www/default/stylen.css
@@ -0,0 +1,164 @@
+/* styles for interface.php and details.php */
+
+html, body {
+ background-color: #a2d2b1;
+ font-family: sans-serif;
+ font-size: 12px;
+}
+html, body, h1, h2, h3, h4, form {
+ margin: 0;
+ padding: 0;
+}
+img {
+ border: 0;
+}
+h2 {
+ font-size: 12px;
+ min-height: 18px;
+ padding-top: 2px;
+ margin-bottom: 10px;
+ background-image: url(grad1.jpg);
+ font-weight: bold;
+ border-bottom: 1px solid #000;
+ text-align: center;
+}
+form {
+ text-align: right;
+}
+label {
+ display: block;
+ text-align: right;
+ margin: 4px 0 4px 0;
+}
+input {
+ margin: 0 5% 0 1%;
+ width: 30%;
+}
+label.wide {
+ text-align: left;
+ padding-left: 5%;
+}
+label.wide input {
+ margin: 0 0 0 0;
+ width: 95%;
+}
+input[type="submit"] {
+ width: auto;
+}
+th {
+ padding: 2px;
+ text-align: left;
+}
+a {
+ color: #0C45FF;
+ text-decoration: none;
+}
+a:visited {
+ color: #0C45FF;
+}
+a:hover {
+ color: #FFFFFF;
+ text-decoration: none;
+}
+
+
+.actions img {
+ width: 16px;
+ height: 16px;
+ margin: 2px;
+}
+
+
+.box {
+ width: 254px;
+ margin: 0 auto;
+ border: 1px solid black;
+ background-color: #d5d5d5;
+ padding-bottom: 10px;
+ margin-bottom: 10px;
+ text-align: center;
+}
+
+
+#top_bar {
+ background-color: #b72916;
+ border: 1px solid #868686;
+ text-align: right;
+}
+
+
+#icon {
+ position: absolute;
+ left: 5px;
+ top: 2px;
+}
+
+
+#header {
+ background-color: #42ac64;
+ padding: 15px 0 5px 140px;
+ font-size: 45px;
+}
+#header strong {
+ text-shadow: 3px 3px 5px #bbb;
+}
+#header small {
+ font-size: 13px;
+}
+
+
+#menu {
+ list-style-type: none;
+ margin: 0;
+ padding: 0 0 0 140px;
+ background-image: url(menu_bg.png);
+ height: 24px;
+}
+#menu li {
+ display: inline;
+ margin-right: 10px;
+}
+#menu a {
+ color: #fff;
+ font-size: 16px;
+}
+#menu a:hover {
+ color: #000;
+ text-decoration: none;
+}
+#menu .shutdown {
+ margin-right: 290px;
+}
+
+
+#sidebar {
+ background: #42AC64;
+ float: right;
+ width: 280px;
+ border-left: 1px solid #000;
+ border-bottom: 1px solid #000;
+ padding: 10px 0 5px 0;
+}
+#sidebar a:hover {
+ color: #000;
+}
+
+
+#content {
+ background: #a2d2b1;
+ text-align: left;
+ margin-right: 280px;
+ padding: 10px;
+}
+#content img { opacity: 0.4; }
+#content a img { opacity: 1.0; }
+
+
+#footer {
+ clear: both;
+ background: #b72916;
+ min-height: 25px;
+ text-align: center;
+ font-size: 14px;
+ padding-top: 5px;
+}
diff --git a/plugins/webinterface/www/default/wz_tooltip.js b/plugins/webinterface/www/default/wz_tooltip.js
new file mode 100644
index 0000000..1329a1b
--- /dev/null
+++ b/plugins/webinterface/www/default/wz_tooltip.js
@@ -0,0 +1,509 @@
+/* This notice must be untouched at all times.
+
+wz_tooltip.js v. 3.45
+
+The latest version is available at
+http://www.walterzorn.com
+or http://www.devira.com
+or http://www.walterzorn.de
+
+Copyright (c) 2002-2005 Walter Zorn. All rights reserved.
+Created 1. 12. 2002 by Walter Zorn (Web: http://www.walterzorn.com )
+Last modified: 22. 1. 2007
+
+Cross-browser tooltips working even in Opera 5 and 6,
+as well as in NN 4, Gecko-Browsers, IE4+, Opera 7+ and Konqueror.
+No onmouseouts required.
+Appearance of tooltips can be individually configured
+via commands within the onmouseovers.
+
+LICENSE: LGPL
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License (LGPL) as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+For more details on the GNU Lesser General Public License,
+see http://www.gnu.org/copyleft/lesser.html
+*/
+
+
+
+//////////////// GLOBAL TOOPTIP CONFIGURATION /////////////////////
+var ttAbove = false; // tooltip above mousepointer? Alternative: true
+var ttBgColor = "#D5D5D5";
+var ttBgImg = ""; // path to background image;
+var ttBorderColor = "#D00000";
+var ttBorderWidth = 1;
+var ttDelay = 350; // time span until tooltip shows up [milliseconds]
+var ttClickClose = false;
+var ttFontColor = "#000000";
+var ttFontFace = "arial,helvetica,sans-serif";
+var ttFontSize = "11px";
+var ttFontWeight = "normal"; // alternative: "bold";
+var ttLeft = false; // tooltip on the left of the mouse? Alternative: true
+var ttOffsetX = 12; // horizontal offset of left-top corner from mousepointer
+var ttOffsetY = 15; // vertical offset "
+var ttOpacity = 70; // opacity of tooltip in percent (must be integer between 0 and 100)
+var ttPadding = 3; // spacing between border and content
+var ttShadowColor = "";
+var ttShadowWidth = 0;
+var ttStatic = false; // tooltip NOT move with the mouse? Alternative: true
+var ttSticky = false; // do NOT hide tooltip on mouseout? Alternative: true
+var ttTemp = 0; // time span after which the tooltip disappears; 0 (zero) means "infinite timespan"
+var ttTextAlign = "left";
+var ttTitleColor = "#ffffff"; // color of caption text
+var ttWidth = 300;
+//////////////////// END OF TOOLTIP CONFIG ////////////////////////
+
+
+
+////////////// TAGS WITH TOOLTIP FUNCTIONALITY ////////////////////
+// List may be extended or shortened:
+var tt_tags = new Array("a","area","b","big","caption","center","code","dd","div","dl","dt","em","h1","h2","h3","h4","h5","h6","i","img","input","li","map","ol","p","pre","s", "select", "small","span","strike","strong","sub","sup","table","td","textarea","th","tr","tt","u","var","ul","layer");
+/////////////////////////////////////////////////////////////////////
+
+
+
+///////// DON'T CHANGE ANYTHING BELOW THIS LINE /////////////////////
+var tt_obj = null, // current tooltip
+tt_ifrm = null, // iframe to cover windowed controls in IE
+tt_objW = 0, tt_objH = 0, // width and height of tt_obj
+tt_objX = 0, tt_objY = 0,
+tt_offX = 0, tt_offY = 0,
+xlim = 0, ylim = 0, // right and bottom borders of visible client area
+tt_sup = false, // true if T_ABOVE cmd
+tt_sticky = false, // tt_obj sticky?
+tt_wait = false,
+tt_act = false, // tooltip visibility flag
+tt_sub = false, // true while tooltip below mousepointer
+tt_u = "undefined",
+tt_mf = null, // stores previous mousemove evthandler
+// Opera: disable href when hovering <a>
+tt_tag = null; // stores hovered dom node, href and previous statusbar txt
+
+
+var tt_db = (document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body? document.body : null,
+tt_n = navigator.userAgent.toLowerCase(),
+tt_nv = navigator.appVersion;
+// Browser flags
+var tt_op = !!(window.opera && document.getElementById),
+tt_op6 = tt_op && !document.defaultView,
+tt_op7 = tt_op && !tt_op6,
+tt_ie = tt_n.indexOf("msie") != -1 && document.all && tt_db && !tt_op,
+tt_ie7 = tt_ie && typeof document.body.style.maxHeight != tt_u,
+tt_ie6 = tt_ie && !tt_ie7 && parseFloat(tt_nv.substring(tt_nv.indexOf("MSIE")+5)) >= 5.5,
+tt_n4 = (document.layers && typeof document.classes != tt_u),
+tt_n6 = (!tt_op && document.defaultView && typeof document.defaultView.getComputedStyle != tt_u),
+tt_w3c = !tt_ie && !tt_n6 && !tt_op && document.getElementById,
+tt_ce = document.captureEvents && !tt_n6;
+
+function tt_Int(t_x)
+{
+ var t_y;
+ return isNaN(t_y = parseInt(t_x))? 0 : t_y;
+}
+function wzReplace(t_x, t_y)
+{
+ var t_ret = "",
+ t_str = this,
+ t_xI;
+ while((t_xI = t_str.indexOf(t_x)) != -1)
+ {
+ t_ret += t_str.substring(0, t_xI) + t_y;
+ t_str = t_str.substring(t_xI + t_x.length);
+ }
+ return t_ret+t_str;
+}
+String.prototype.wzReplace = wzReplace;
+function tt_N4Tags(tagtyp, t_d, t_y)
+{
+ t_d = t_d || document;
+ t_y = t_y || new Array();
+ var t_x = (tagtyp=="a")? t_d.links : t_d.layers;
+ for(var z = t_x.length; z--;) t_y[t_y.length] = t_x[z];
+ for(z = t_d.layers.length; z--;) t_y = tt_N4Tags(tagtyp, t_d.layers[z].document, t_y);
+ return t_y;
+}
+function tt_Htm(tt, t_id, txt)
+{
+ var t_bgc = (typeof tt.T_BGCOLOR != tt_u)? tt.T_BGCOLOR : ttBgColor,
+ t_bgimg = (typeof tt.T_BGIMG != tt_u)? tt.T_BGIMG : ttBgImg,
+ t_bc = (typeof tt.T_BORDERCOLOR != tt_u)? tt.T_BORDERCOLOR : ttBorderColor,
+ t_bw = (typeof tt.T_BORDERWIDTH != tt_u)? tt.T_BORDERWIDTH : ttBorderWidth,
+ t_ff = (typeof tt.T_FONTFACE != tt_u)? tt.T_FONTFACE : ttFontFace,
+ t_fc = (typeof tt.T_FONTCOLOR != tt_u)? tt.T_FONTCOLOR : ttFontColor,
+ t_fsz = (typeof tt.T_FONTSIZE != tt_u)? tt.T_FONTSIZE : ttFontSize,
+ t_fwght = (typeof tt.T_FONTWEIGHT != tt_u)? tt.T_FONTWEIGHT : ttFontWeight,
+ t_opa = (typeof tt.T_OPACITY != tt_u)? tt.T_OPACITY : ttOpacity,
+ t_padd = (typeof tt.T_PADDING != tt_u)? tt.T_PADDING : ttPadding,
+ t_shc = (typeof tt.T_SHADOWCOLOR != tt_u)? tt.T_SHADOWCOLOR : (ttShadowColor || 0),
+ t_shw = (typeof tt.T_SHADOWWIDTH != tt_u)? tt.T_SHADOWWIDTH : (ttShadowWidth || 0),
+ t_algn = (typeof tt.T_TEXTALIGN != tt_u)? tt.T_TEXTALIGN : ttTextAlign,
+ t_tit = (typeof tt.T_TITLE != tt_u)? tt.T_TITLE : "",
+ t_titc = (typeof tt.T_TITLECOLOR != tt_u)? tt.T_TITLECOLOR : ttTitleColor,
+ t_w = (typeof tt.T_WIDTH != tt_u)? tt.T_WIDTH : ttWidth;
+ if(t_shc || t_shw)
+ {
+ t_shc = t_shc || "#c0c0c0";
+ t_shw = t_shw || 5;
+ }
+ if(tt_n4 && (t_fsz == "10px" || t_fsz == "11px")) t_fsz = "12px";
+
+ var t_optx = (tt_n4? '' : tt_n6? ('-moz-opacity:'+(t_opa/100.0)) : tt_ie? ('filter:Alpha(opacity='+t_opa+')') : ('opacity:'+(t_opa/100.0))) + ';';
+ var t_y = '<div id="'+t_id+'" style="position:absolute;z-index:1010;';
+ t_y += 'left:0px;top:0px;width:'+(t_w+t_shw)+'px;visibility:'+(tt_n4? 'hide' : 'hidden')+';'+t_optx+'">' +
+ '<table border="0" cellpadding="0" cellspacing="0"'+(t_bc? (' bgcolor="'+t_bc+'" style="background:'+t_bc+';"') : '')+' width="'+t_w+'">';
+ if(t_tit)
+ {
+ t_y += '<tr><td style="padding-left:3px;padding-right:3px;" align="'+t_algn+'"><font color="'+t_titc+'" face="'+t_ff+'" ' +
+ 'style="color:'+t_titc+';font-family:'+t_ff+';font-size:'+t_fsz+';"><b>' +
+ (tt_n4? '&nbsp;' : '')+t_tit+'</b></font></td></tr>';
+ }
+ t_y += '<tr><td><table border="0" cellpadding="'+t_padd+'" cellspacing="'+t_bw+'" width="100%">' +
+ '<tr><td'+(t_bgc? (' bgcolor="'+t_bgc+'"') : '')+(t_bgimg? ' background="'+t_bgimg+'"' : '')+' style="text-align:'+t_algn+';';
+ if(tt_n6) t_y += 'padding:'+t_padd+'px;';
+ t_y += '" align="'+t_algn+'"><font color="'+t_fc+'" face="'+t_ff+'"' +
+ ' style="color:'+t_fc+';font-family:'+t_ff+';font-size:'+t_fsz+';font-weight:'+t_fwght+';">';
+ if(t_fwght == 'bold') t_y += '<b>';
+ t_y += txt;
+ if(t_fwght == 'bold') t_y += '</b>';
+ t_y += '</font></td></tr></table></td></tr></table>';
+ if(t_shw)
+ {
+ var t_spct = Math.round(t_shw*1.3);
+ if(tt_n4)
+ {
+ t_y += '<layer bgcolor="'+t_shc+'" left="'+t_w+'" top="'+t_spct+'" width="'+t_shw+'" height="0"></layer>' +
+ '<layer bgcolor="'+t_shc+'" left="'+t_spct+'" align="bottom" width="'+(t_w-t_spct)+'" height="'+t_shw+'"></layer>';
+ }
+ else
+ {
+ t_optx = tt_n6? '-moz-opacity:0.85;' : tt_ie? 'filter:Alpha(opacity=85);' : 'opacity:0.85;';
+ t_y += '<div id="'+t_id+'R" style="position:absolute;background:'+t_shc+';left:'+t_w+'px;top:'+t_spct+'px;width:'+t_shw+'px;height:1px;overflow:hidden;'+t_optx+'"></div>' +
+ '<div style="position:relative;background:'+t_shc+';left:'+t_spct+'px;top:0px;width:'+(t_w-t_spct)+'px;height:'+t_shw+'px;overflow:hidden;'+t_optx+'"></div>';
+ }
+ }
+ return(t_y+'</div>');
+}
+function tt_EvX(t_e)
+{
+ var t_y = tt_Int(t_e.pageX || t_e.clientX || 0) +
+ tt_Int(tt_ie? tt_db.scrollLeft : 0) +
+ tt_offX;
+ if(t_y > xlim) t_y = xlim;
+ var t_scr = tt_Int(window.pageXOffset || (tt_db? tt_db.scrollLeft : 0) || 0);
+ if(t_y < t_scr) t_y = t_scr;
+ return t_y;
+}
+function tt_EvY(t_e)
+{
+ var t_y2;
+
+ var t_y = tt_Int(t_e.pageY || t_e.clientY || 0) +
+ tt_Int(tt_ie? tt_db.scrollTop : 0);
+ if(tt_sup && (t_y2 = t_y - (tt_objH + tt_offY - 15)) >= tt_Int(window.pageYOffset || (tt_db? tt_db.scrollTop : 0) || 0))
+ t_y -= (tt_objH + tt_offY - 15);
+ else if(t_y > ylim || !tt_sub && t_y > ylim-24)
+ {
+ t_y -= (tt_objH + 5);
+ tt_sub = false;
+ }
+ else
+ {
+ t_y += tt_offY;
+ tt_sub = true;
+ }
+ return t_y;
+}
+function tt_ReleasMov()
+{
+ if(document.onmousemove == tt_Move)
+ {
+ if(!tt_mf && tt_ce) document.releaseEvents(Event.MOUSEMOVE);
+ document.onmousemove = tt_mf;
+ }
+}
+function tt_ShowIfrm(t_x)
+{
+ if(!tt_obj || !tt_ifrm) return;
+ if(t_x)
+ {
+ tt_ifrm.style.width = tt_objW+'px';
+ tt_ifrm.style.height = tt_objH+'px';
+ tt_ifrm.style.display = "block";
+ }
+ else tt_ifrm.style.display = "none";
+}
+function tt_GetDiv(t_id)
+{
+ return(
+ tt_n4? (document.layers[t_id] || null)
+ : tt_ie? (document.all[t_id] || null)
+ : (document.getElementById(t_id) || null)
+ );
+}
+function tt_GetDivW()
+{
+ return tt_Int(
+ tt_n4? tt_obj.clip.width
+ : (tt_obj.style.pixelWidth || tt_obj.offsetWidth)
+ );
+}
+function tt_GetDivH()
+{
+ return tt_Int(
+ tt_n4? tt_obj.clip.height
+ : (tt_obj.style.pixelHeight || tt_obj.offsetHeight)
+ );
+}
+
+// Compat with DragDrop Lib: Ensure that z-index of tooltip is lifted beyond toplevel dragdrop element
+function tt_SetDivZ()
+{
+ var t_i = tt_obj.style || tt_obj;
+ if(t_i)
+ {
+ if(window.dd && dd.z)
+ t_i.zIndex = Math.max(dd.z+1, t_i.zIndex);
+ if(tt_ifrm) tt_ifrm.style.zIndex = t_i.zIndex-1;
+ }
+}
+function tt_SetDivPos(t_x, t_y)
+{
+ var t_i = tt_obj.style || tt_obj;
+ var t_px = (tt_op6 || tt_n4)? '' : 'px';
+ t_i.left = (tt_objX = t_x) + t_px;
+ t_i.top = (tt_objY = t_y) + t_px;
+ // window... to circumvent the FireFox Alzheimer Bug
+ if(window.tt_ifrm)
+ {
+ tt_ifrm.style.left = t_i.left;
+ tt_ifrm.style.top = t_i.top;
+ }
+}
+function tt_ShowDiv(t_x)
+{
+ tt_ShowIfrm(t_x);
+ if(tt_n4) tt_obj.visibility = t_x? 'show' : 'hide';
+ else tt_obj.style.visibility = t_x? 'visible' : 'hidden';
+ tt_act = t_x;
+}
+function tt_DeAlt(t_tag)
+{
+ if(t_tag)
+ {
+ if(t_tag.alt) t_tag.alt = "";
+ if(t_tag.title) t_tag.title = "";
+ var t_c = t_tag.children || t_tag.childNodes || null;
+ if(t_c)
+ {
+ for(var t_i = t_c.length; t_i; )
+ tt_DeAlt(t_c[--t_i]);
+ }
+ }
+}
+function tt_OpDeHref(t_e)
+{
+ var t_tag;
+ if(t_e)
+ {
+ t_tag = t_e.target;
+ while(t_tag)
+ {
+ if(t_tag.hasAttribute("href"))
+ {
+ tt_tag = t_tag
+ tt_tag.t_href = tt_tag.getAttribute("href");
+ tt_tag.removeAttribute("href");
+ tt_tag.style.cursor = "hand";
+ tt_tag.onmousedown = tt_OpReHref;
+ tt_tag.stats = window.status;
+ window.status = tt_tag.t_href;
+ break;
+ }
+ t_tag = t_tag.parentElement;
+ }
+ }
+}
+function tt_OpReHref()
+{
+ if(tt_tag)
+ {
+ tt_tag.setAttribute("href", tt_tag.t_href);
+ window.status = tt_tag.stats;
+ tt_tag = null;
+ }
+}
+function tt_Show(t_e, t_id, t_sup, t_clk, t_delay, t_fix, t_left, t_offx, t_offy, t_static, t_sticky, t_temp)
+{
+ if(tt_obj) tt_Hide();
+ tt_mf = document.onmousemove || null;
+ if(window.dd && (window.DRAG && tt_mf == DRAG || window.RESIZE && tt_mf == RESIZE)) return;
+ var t_sh, t_h;
+
+ tt_obj = tt_GetDiv(t_id);
+ if(tt_obj)
+ {
+ t_e = t_e || window.event;
+ tt_sub = !(tt_sup = t_sup);
+ tt_sticky = t_sticky;
+ tt_objW = tt_GetDivW();
+ tt_objH = tt_GetDivH();
+ tt_offX = t_left? -(tt_objW+t_offx) : t_offx;
+ tt_offY = t_offy;
+ if(tt_op7) tt_OpDeHref(t_e);
+ if(tt_n4)
+ {
+ if(tt_obj.document.layers.length)
+ {
+ t_sh = tt_obj.document.layers[0];
+ t_sh.clip.height = tt_objH - Math.round(t_sh.clip.width*1.3);
+ }
+ }
+ else
+ {
+ t_sh = tt_GetDiv(t_id+'R');
+ if(t_sh)
+ {
+ t_h = tt_objH - tt_Int(t_sh.style.pixelTop || t_sh.style.top || 0);
+ if(typeof t_sh.style.pixelHeight != tt_u) t_sh.style.pixelHeight = t_h;
+ else t_sh.style.height = t_h+'px';
+ }
+ }
+
+ xlim = tt_Int((tt_db && tt_db.clientWidth)? tt_db.clientWidth : window.innerWidth) +
+ tt_Int(window.pageXOffset || (tt_db? tt_db.scrollLeft : 0) || 0) -
+ tt_objW -
+ (tt_n4? 21 : 0);
+ ylim = tt_Int(window.innerHeight || tt_db.clientHeight) +
+ tt_Int(window.pageYOffset || (tt_db? tt_db.scrollTop : 0) || 0) -
+ tt_objH - tt_offY;
+
+ tt_SetDivZ();
+ if(t_fix) tt_SetDivPos(tt_Int((t_fix = t_fix.split(','))[0]), tt_Int(t_fix[1]));
+ else tt_SetDivPos(tt_EvX(t_e), tt_EvY(t_e));
+
+ var t_txt = 'tt_ShowDiv(\'true\');';
+ if(t_sticky) t_txt += '{'+
+ 'tt_ReleasMov();'+
+ (t_clk? ('window.tt_upFunc = document.onmouseup || null;'+
+ 'if(tt_ce) document.captureEvents(Event.MOUSEUP);'+
+ 'document.onmouseup = new Function("window.setTimeout(\'tt_Hide();\', 10);");') : '')+
+ '}';
+ else if(t_static) t_txt += 'tt_ReleasMov();';
+ if(t_temp > 0) t_txt += 'window.tt_rtm = window.setTimeout(\'tt_sticky = false; tt_Hide();\','+t_temp+');';
+ window.tt_rdl = window.setTimeout(t_txt, t_delay);
+
+ if(!t_fix)
+ {
+ if(tt_ce) document.captureEvents(Event.MOUSEMOVE);
+ document.onmousemove = tt_Move;
+ }
+ }
+}
+var tt_area = false;
+function tt_Move(t_ev)
+{
+ if(!tt_obj) return;
+ if(tt_n6 || tt_w3c)
+ {
+ if(tt_wait) return;
+ tt_wait = true;
+ setTimeout('tt_wait = false;', 5);
+ }
+ var t_e = t_ev || window.event;
+ tt_SetDivPos(tt_EvX(t_e), tt_EvY(t_e));
+ if(window.tt_op6)
+ {
+ if(tt_area && t_e.target.tagName != 'AREA') tt_Hide();
+ else if(t_e.target.tagName == 'AREA') tt_area = true;
+ }
+}
+function tt_Hide()
+{
+ if(window.tt_obj)
+ {
+ if(window.tt_rdl) window.clearTimeout(tt_rdl);
+ if(!tt_sticky || !tt_act)
+ {
+ if(window.tt_rtm) window.clearTimeout(tt_rtm);
+ tt_ShowDiv(false);
+ tt_SetDivPos(-tt_objW, -tt_objH);
+ tt_obj = null;
+ if(typeof window.tt_upFunc != tt_u) document.onmouseup = window.tt_upFunc;
+ }
+ tt_sticky = false;
+ if(tt_op6 && tt_area) tt_area = false;
+ tt_ReleasMov();
+ if(tt_op7) tt_OpReHref();
+ }
+}
+function tt_Init()
+{
+ if(!(tt_op || tt_n4 || tt_n6 || tt_ie || tt_w3c)) return;
+
+ var htm = tt_n4? '<div style="position:absolute;"></div>' : '',
+ tags,
+ t_tj,
+ over,
+ t_b,
+ esc = 'return escape(';
+ for(var i = tt_tags.length; i;)
+ {--i;
+ tags = tt_ie? (document.all.tags(tt_tags[i]) || 1)
+ : document.getElementsByTagName? (document.getElementsByTagName(tt_tags[i]) || 1)
+ : (!tt_n4 && tt_tags[i]=="a")? document.links
+ : 1;
+ if(tt_n4 && (tt_tags[i] == "a" || tt_tags[i] == "layer")) tags = tt_N4Tags(tt_tags[i]);
+ for(var j = tags.length; j;)
+ {--j;
+ if(typeof (t_tj = tags[j]).onmouseover == "function" && t_tj.onmouseover.toString().indexOf(esc) != -1 && !tt_n6 || tt_n6 && (over = t_tj.getAttribute("onmouseover")) && over.indexOf(esc) != -1)
+ {
+ if(over) t_tj.onmouseover = new Function(over);
+ var txt = unescape(t_tj.onmouseover());
+ htm += tt_Htm(
+ t_tj,
+ "tOoLtIp"+i+""+j,
+ txt.wzReplace("& ","&")
+ );
+ // window. to circumvent the FF Alzheimer Bug
+ t_tj.onmouseover = new Function('e',
+ 'if(window.tt_Show && tt_Show) tt_Show(e,'+
+ '"tOoLtIp' +i+''+j+ '",'+
+ ((typeof t_tj.T_ABOVE != tt_u)? t_tj.T_ABOVE : ttAbove)+','+
+ ((typeof t_tj.T_CLICKCLOSE != tt_u)? t_tj.T_CLICKCLOSE : ttClickClose)+','+
+ ((typeof t_tj.T_DELAY != tt_u)? t_tj.T_DELAY : ttDelay)+','+
+ ((typeof t_tj.T_FIX != tt_u)? '"'+t_tj.T_FIX+'"' : '""')+','+
+ ((typeof t_tj.T_LEFT != tt_u)? t_tj.T_LEFT : ttLeft)+','+
+ ((typeof t_tj.T_OFFSETX != tt_u)? t_tj.T_OFFSETX : ttOffsetX)+','+
+ ((typeof t_tj.T_OFFSETY != tt_u)? t_tj.T_OFFSETY : ttOffsetY)+','+
+ ((typeof t_tj.T_STATIC != tt_u)? t_tj.T_STATIC : ttStatic)+','+
+ ((typeof t_tj.T_STICKY != tt_u)? t_tj.T_STICKY : ttSticky)+','+
+ ((typeof t_tj.T_TEMP != tt_u)? t_tj.T_TEMP : ttTemp)+
+ ');'
+ );
+ t_tj.onmouseout = tt_Hide;
+ tt_DeAlt(t_tj);
+ }
+ }
+ }
+ if(tt_ie6) htm += '<iframe id="TTiEiFrM" src="javascript:false" scrolling="no" frameborder="0" style="filter:Alpha(opacity=0);position:absolute;top:0px;left:0px;display:none;"></iframe>';
+ t_b = document.getElementsByTagName? document.getElementsByTagName("body")[0] : tt_db;
+ if(t_b && t_b.insertAdjacentHTML) t_b.insertAdjacentHTML("AfterBegin", htm);
+ else if(t_b && typeof t_b.innerHTML != tt_u && document.createElement && t_b.appendChild)
+ {
+ var t_el = document.createElement("div");
+ t_b.appendChild(t_el);
+ t_el.innerHTML = htm;
+ }
+ else
+ document.write(htm);
+ if(document.getElementById) tt_ifrm = document.getElementById("TTiEiFrM");
+}
+tt_Init();
diff --git a/plugins/webinterface/www/mobile/Makefile.am b/plugins/webinterface/www/mobile/Makefile.am
new file mode 100644
index 0000000..3aae8b4
--- /dev/null
+++ b/plugins/webinterface/www/mobile/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+
+ktdatadir = $(kde_datadir)/ktorrent/www/mobile
+
+
+
+ktdata_DATA = favicon.ico interface.php ktorrentwebinterfacelogo.png login.html remove.png start.png stop.png settings.php torrent.php
diff --git a/plugins/webinterface/www/mobile/favicon.ico b/plugins/webinterface/www/mobile/favicon.ico
new file mode 100644
index 0000000..3213b23
--- /dev/null
+++ b/plugins/webinterface/www/mobile/favicon.ico
Binary files differ
diff --git a/plugins/webinterface/www/mobile/interface.php b/plugins/webinterface/www/mobile/interface.php
new file mode 100644
index 0000000..57582de
--- /dev/null
+++ b/plugins/webinterface/www/mobile/interface.php
@@ -0,0 +1,113 @@
+<html>
+<head>
+<title>KTorrent WebInterface</title>
+</head>
+<body>
+<table width="100%">
+ <tbody>
+ <tr>
+ <td align="center"><IMG src="ktorrentwebinterfacelogo.png" width="340" height="150" align="top" border="0"></td>
+
+ <td><strong>ktorrent</strong>->transfers</td>
+
+ <td><td><a href="interface.php" >refresh</a></td>
+ </tr>
+ </tbody>
+</table>
+<hr>
+<table width="100%">
+ <tbody>
+ <?php
+ $stats=downloadStatus();
+ $a = 0;
+ foreach ($stats as $torrent) {
+ echo "<tr>";
+ $perc = round(100.0 - ($torrent['bytes_left_to_download'] / $torrent['total_bytes_to_download']) * 100.0, 2);
+ echo "<td><a href=\"torrent.php?id=$a\" >{$torrent['torrent_name']}</a></td>";
+ switch ($torrent['status']) {
+ case 0:
+ echo "<td>Not Started</td>";
+ break;
+ case 1:
+ echo "<td>Seeding Complete</td>";
+ break;
+ case 2:
+ echo "<td>Download Complete</td>";
+ break;
+ case 3:
+ echo "<td>Seeding</td>";
+ break;
+ case 4:
+ echo "<td>Downloading</td>";
+ break;
+ case 5:
+ echo "<td>Stalled</td>";
+ break;
+ case 6:
+ echo "<td>Stopped</td>";
+ break;
+ case 7:
+ echo "<td>Allocating Diskspace</td>";
+ break;
+ case 8:
+ echo "<td>Error</td>";
+ break;
+ case 9:
+ echo "<td>Queued</td>";
+ break;
+ case 10:
+ echo "<td>Checking Data</td>";
+ break;
+ default:
+ echo "<td>Not supported Status</td>";
+ }
+ echo "<td>$perc%</td>";
+ $a=$a+1;
+ echo "</tr>";
+ }
+ ?>
+
+ <tr>
+ <td>&nbsp;</td>
+ <td><hr></td>
+ <td>&nbsp;</td>
+ </tr>
+
+ <tr>
+ <?php
+ $globalinfo=globalInfo();
+ echo "<td><strong>Speed</strong></td>";
+ echo "<td>Down: {$globalinfo['download_speed']}</td>";
+ echo "<td>Up: {$globalinfo['upload_speed']}</td>";
+ ?>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td><hr></td>
+ <td>&nbsp;</td>
+ </tr>
+ <tr>
+ <td><a href="interface.php?startall=startall" ><strong>Start All</strong></a></td>
+ <td>&nbsp;</td>
+ <td><a href="interface.php?stopall=stopall" ><strong>Stop All</strong></a></td>
+ </tr>
+ <tr>
+ <td>&nbsp;</td>
+ <td><a href="settings.php" ><strong>Settings</strong></a></td>
+ <td>&nbsp;</td>
+ </tr>
+
+ </tbody>
+</table>
+<FORM method="GET">
+<INPUT type="text" name="load_torrent">
+<INPUT type="submit" name="Load torrent" value="Load torrent"></tr>
+</FORM>
+<FORM method="post" enctype="multipart/form-data" action="interface.php">
+Local File:<INPUT type="file" name="load_torrent">
+<INPUT type="submit" name="Upload Torrent" value="Upload Torrent"></tr>
+</FORM>
+
+</body>
+</html>
+
diff --git a/plugins/webinterface/www/mobile/ktorrentwebinterfacelogo.png b/plugins/webinterface/www/mobile/ktorrentwebinterfacelogo.png
new file mode 100644
index 0000000..bc235b5
--- /dev/null
+++ b/plugins/webinterface/www/mobile/ktorrentwebinterfacelogo.png
Binary files differ
diff --git a/plugins/webinterface/www/mobile/login.html b/plugins/webinterface/www/mobile/login.html
new file mode 100644
index 0000000..359c44c
--- /dev/null
+++ b/plugins/webinterface/www/mobile/login.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+ <title>KTorrent WebInterface - Login</title>
+ <meta name="GENERATOR" content="Quanta Plus">
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+<body>
+<TABLE align="center" >
+<tr><td><IMG src="ktorrentwebinterfacelogo.png" width="687" height="300" align="left" border="0"></td></tr>
+</TABLE>
+<TABLE align="center" >
+<FORM action="interface.php" method="POST">
+
+ <tr><td>Username</td><td><INPUT type="text" name="username"></td></tr>
+ <tr><td>Password</td><td><INPUT type="password" name="password"></td></tr>
+<tr><td></td><td><INPUT type="submit" name="Login"></td></tr>
+
+</FORM>
+</TABLE>
+
+</body>
+</html>
diff --git a/plugins/webinterface/www/mobile/remove.png b/plugins/webinterface/www/mobile/remove.png
new file mode 100644
index 0000000..a7080ac
--- /dev/null
+++ b/plugins/webinterface/www/mobile/remove.png
Binary files differ
diff --git a/plugins/webinterface/www/mobile/settings.php b/plugins/webinterface/www/mobile/settings.php
new file mode 100644
index 0000000..b763bb1
--- /dev/null
+++ b/plugins/webinterface/www/mobile/settings.php
@@ -0,0 +1,44 @@
+<html>
+<head>
+<title>KTorrent WebInterface</title>
+</head>
+<body>
+<table width="100%">
+ <tbody>
+ <tr>
+ <td align="center"><IMG src="ktorrentwebinterfacelogo.png" width="340" height="150" align="top" border="0"></td>
+ <td><strong>ktorrent-><a href="interface.php">transfers</a></strong>->settings</td>
+ <td><a href="settings.php" >refresh</a></td>
+ </tr>
+ </tbody>
+</table>
+<table width="100%">
+ <tbody>
+<?php
+ $globalinfo=globalInfo();
+ echo "<FORM method=\"GET\">";
+ echo "<tr>";
+ echo "<td>Upload Speed (0 is no limit): </td>";
+ echo "<td><INPUT type=\"text\" name=\"maximum_upload_rate\" value=\"{$globalinfo['max_upload_speed']}\"></td>";
+ echo " </tr>";
+ echo "<tr>";
+ echo "<td>Download Speed (0 is no limit): </td>";
+ echo "<td><INPUT type=\"text\" name=\"maximum_download_rate\" value=\"{$globalinfo['max_download_speed']}\"></td>";
+ echo "</tr>";
+ echo "<tr>";
+ echo "<td>Maximum downloads (0 is no limit): </td>";
+ echo "<td><INPUT type=\"text\" name=\"maximum_downloads\" value=\"{$globalinfo['max_downloads']}\"></td>";
+ echo"</tr>";
+ echo "<tr>";
+ echo "<td>Maximum seeds (0 is no limit): </td>";
+ echo "<td><INPUT type=\"text\" name=\"maximum_seeds\" value=\"{$globalinfo['max_seeds']}\"></td>";
+ echo"</tr>";
+ echo "<tr><td><INPUT type=\"submit\"></tr></td>";
+ echo "</FORM>";
+?>
+</tbody>
+</table>
+
+</body>
+</html>
+
diff --git a/plugins/webinterface/www/mobile/start.png b/plugins/webinterface/www/mobile/start.png
new file mode 100644
index 0000000..ead5c73
--- /dev/null
+++ b/plugins/webinterface/www/mobile/start.png
Binary files differ
diff --git a/plugins/webinterface/www/mobile/stop.png b/plugins/webinterface/www/mobile/stop.png
new file mode 100644
index 0000000..7c6d824
--- /dev/null
+++ b/plugins/webinterface/www/mobile/stop.png
Binary files differ
diff --git a/plugins/webinterface/www/mobile/torrent.php b/plugins/webinterface/www/mobile/torrent.php
new file mode 100644
index 0000000..a1e451d
--- /dev/null
+++ b/plugins/webinterface/www/mobile/torrent.php
@@ -0,0 +1,91 @@
+<html>
+<head>
+<title>KTorrent WebInterface</title>
+</head>
+<body>
+<table width="100%">
+ <tbody>
+ <tr>
+ <td align="center"><IMG src="ktorrentwebinterfacelogo.png" width="340" height="150" align="top" border="0"></td>
+ <?php
+ $stats=downloadStatus();
+ $t=$stats[$_REQUEST['id']];
+ echo "<td><strong>ktorrent-><a href=\"interface.php\">transfers</a></strong>->{$t['torrent_name']}</td>";
+ echo "<td><a href=\"torrent.php?id={$_REQUEST['id']}\" >refresh</a></td>";
+ ?>
+ </tr>
+ </tbody>
+</table>
+<table width="100%">
+ <tbody>
+ <tr>
+ <?php
+ echo "<td><a href=\"torrent.php?stop={$_REQUEST['id']}&id={$_REQUEST['id']}\" title=\"STOP\"><img src=\"/stop.png\" name=\"stop\" width=\"16\" height=\"16\" border=\"0\"></a></td>";
+ echo "<td><a href=\"torrent.php?start={$_REQUEST['id']}&id={$_REQUEST['id']}\" title=\"START\"><img src=\"/start.png\" name=\"start\" width=\"16\" height=\"16\" border=\"0\"></a></td>";
+ echo "<td><a href=\"interface.php?remove={$_REQUEST['id']}\" title=\"REMOVE\"><img src=\"/remove.png\" name=\"remove\" width=\"16\" height=\"16\" border=\"0\"></a></td>";
+ ?>
+ </tr>
+ </tbody>
+</table>
+<table width="100%">
+ <tbody>
+ <?php
+ echo "<tr>";
+ echo "<td><strong>Status: </strong></td>";
+ switch ($t['status']) {
+ case 0:
+ echo "<td>NOT_STARTED</td>";
+ break;
+ case 1:
+ echo "<td>SEEDING_COMPLETE</td>";
+ break;
+ case 2:
+ echo "<td>DOWNLOAD_COMPLETE</td>";
+ break;
+ case 3:
+ echo "<td>SEEDING</td>";
+ break;
+ case 4:
+ echo "<td>DOWNLOADING</td>";
+ break;
+ case 5:
+ echo "<td>STALLED</td>";
+ break;
+ case 6:
+ echo "<td>STOPPED</td>";
+ break;
+ case 7:
+ echo "<td>ALLOCATING_DISKSPACE</td>";
+ break;
+ case 8:
+ echo "<td>ERROR</td>";
+ break;
+ case 9:
+ echo "<td>QUEUED</td>";
+ break;
+ case 10:
+ echo "<td>CHECKING_DATA</td>";
+ break;
+ default:
+ echo "<td>Not supported Status</td>";
+ }
+ echo "</tr>";
+ echo "<tr>";
+ echo "<td><strong>Down speed: </strong></td>";
+ echo "<td>{$t['download_rate']}</td>";
+ echo "</tr>";
+ echo "<tr>";
+ echo "<td><strong>Up speed: </strong></td>";
+ echo "<td>{$t['upload_rate']}</td>";
+ echo "</tr>";
+ echo "<tr>";
+ echo "<td><strong>Complete: </strong></td>";
+ $perc = round(100.0 - ($t['bytes_left_to_download'] / $t['total_bytes_to_download']) * 100.0, 2);
+ echo "<td>$perc %</td>";
+ echo "</tr>";
+ ?>
+ </tbody>
+</table>
+</body>
+</html>
+
diff --git a/plugins/zeroconf/Makefile.am b/plugins/zeroconf/Makefile.am
new file mode 100644
index 0000000..c6870e6
--- /dev/null
+++ b/plugins/zeroconf/Makefile.am
@@ -0,0 +1,22 @@
+INCLUDES = -I$(top_builddir)/libktorrent -I$(top_builddir)/ktorrent/libktorrent \
+ -I$(srcdir)/../../libktorrent $(all_includes)
+
+METASOURCES = AUTO
+
+if COMPILE_ZEROCONF
+kde_module_LTLIBRARIES = ktzeroconfplugin.la
+endif
+
+ktzeroconfplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) $(AVAHI_LIBS) $(AVAHI_QT3_LIBS)
+ktzeroconfplugin_la_SOURCES = localbrowser.cpp avahiservice.cpp \
+ zeroconfplugin.cpp
+
+ktzeroconfplugin_la_LIBADD = $(LIB_KDECORE) $(LIB_KFILE) $(LIB_KPARTS) $(LIB_KDEUI)\
+ $(LIB_QT) ../../libktorrent/libktorrent.la
+
+noinst_HEADERS = zeroconfplugin.h
+
+kde_services_DATA = ktzeroconfplugin.desktop
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
+
+
diff --git a/plugins/zeroconf/avahiservice.cpp b/plugins/zeroconf/avahiservice.cpp
new file mode 100644
index 0000000..a190d8e
--- /dev/null
+++ b/plugins/zeroconf/avahiservice.cpp
@@ -0,0 +1,341 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Lesly Weyts and Kevin Andre *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <util/log.h>
+#include <torrent/peerid.h>
+#include <avahi-common/watch.h>
+#include <avahi-qt3/qt-watch.h>
+#include "localbrowser.h"
+#include "avahiservice.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ void group_callback(AvahiEntryGroup* g, AvahiEntryGroupState state, void* userdata)
+ {
+ AvahiService* service = reinterpret_cast<AvahiService*>(userdata);
+
+ if (g == service->group)
+ {
+ switch (state)
+ {
+ case AVAHI_ENTRY_GROUP_ESTABLISHED:
+ break;
+ case AVAHI_ENTRY_GROUP_COLLISION:
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Entry group collision." << endl;
+ break;
+ case AVAHI_ENTRY_GROUP_FAILURE:
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Entry group failure." << endl;
+ break;
+ case AVAHI_ENTRY_GROUP_UNCOMMITED:
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Entry group uncommited." << endl;
+ break;
+ case AVAHI_ENTRY_GROUP_REGISTERING:
+ ;
+ }
+ }
+ }
+
+ void publish_service(AvahiService* service, AvahiClient *c)
+ {
+ assert(c);
+
+ if (!service->group)
+ {
+ if (!(service->group = avahi_entry_group_new(c, group_callback, service)))
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: avahi_entry_group_new failed." << endl;
+ return;
+ }
+ }
+
+ const char* name = avahi_strdup(QString("%1__%2%3").arg(service->id).arg((rand() % 26) + 65).arg((rand() % 26) + 65).ascii());
+ const char* type = avahi_strdup("_bittorrent._tcp");
+ const char* subtype = avahi_strdup(QString("_" + service->infoHash + "._sub._bittorrent._tcp").ascii());
+
+ if (avahi_entry_group_add_service(
+ service->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ (AvahiPublishFlags)0, name, type, NULL, NULL, service->port, NULL))
+ {
+ if (avahi_client_errno(c) != -8)
+ Out(SYS_ZCO|LOG_DEBUG) << QString("ZC: Failed to add the service (%i).").arg(avahi_client_errno(c)) << endl;
+ else
+ publish_service(service, c);
+ return;
+ }
+
+ if (avahi_entry_group_add_service_subtype(
+ service->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ (AvahiPublishFlags)0, name, type, NULL, subtype))
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << QString("ZC: Failed to add the service subtype (%i).").arg( avahi_client_errno(c)) << endl;
+ return;
+ }
+
+ if (avahi_entry_group_commit(service->group))
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Failed to commit the entry group." << endl;
+ return;
+ }
+ }
+
+ void publisher_callback(AvahiClient* c, AvahiClientState state, void* userdata)
+ {
+ if (!c)
+ return;
+
+ AvahiService* service = reinterpret_cast<AvahiService*>(userdata);
+
+ switch (state) {
+ case AVAHI_CLIENT_S_RUNNING:
+ {
+ if (!service->group)
+ publish_service(service, c);
+ break;
+ }
+ case AVAHI_CLIENT_FAILURE:
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "Failure when publishing." << endl;
+ break;
+ }
+ case AVAHI_CLIENT_S_COLLISION:
+ case AVAHI_CLIENT_S_REGISTERING:
+ {
+ if (service->group)
+ avahi_entry_group_reset(service->group);
+ break;
+ }
+ case AVAHI_CLIENT_CONNECTING:
+ ;
+ }
+ }
+
+ void listener_callback(AvahiClient* c, AvahiClientState state, void* userdata)
+ {
+ assert(c);
+
+ AvahiService* service = reinterpret_cast<AvahiService*>(userdata);
+
+ if (state == AVAHI_CLIENT_FAILURE)
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Server connection failure." << endl;
+ }
+ }
+
+ void resolve_callback(
+ AvahiServiceResolver* r,
+ AVAHI_GCC_UNUSED AvahiIfIndex interface,
+ AVAHI_GCC_UNUSED AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char* name,
+ AVAHI_GCC_UNUSED const char* type,
+ AVAHI_GCC_UNUSED const char* domain,
+ AVAHI_GCC_UNUSED const char* host_name,
+ const AvahiAddress* address,
+ uint16_t port,
+ AVAHI_GCC_UNUSED AvahiStringList* txt,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ void* userdata)
+ {
+ assert(r);
+
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Resolver failed." << endl;
+ break;
+ }
+ case AVAHI_RESOLVER_FOUND:
+ {
+ AvahiService* service = reinterpret_cast<AvahiService*>(userdata);
+
+ QString realname = QString(name);
+ realname.truncate(realname.length() - 5);
+
+ if (service->id != QString(realname))
+ {
+ char a[AVAHI_ADDRESS_STR_MAX];
+ avahi_address_snprint(a, sizeof(a), address);
+ const char* ip = a;
+ LocalBrowser::insert(bt::PeerID(realname.ascii()));
+
+ Out(SYS_ZCO|LOG_NOTICE) << "ZC: found local peer " << ip << ":" << port << endl;
+ service->addPeer(ip,port,true);
+ service->emitPeersReady();
+ }
+ }
+ }
+ avahi_service_resolver_free(r);
+ }
+
+ void browser_callback(
+ AvahiServiceBrowser* b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char* name,
+ const char* type,
+ const char* domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ void* userdata)
+ {
+ assert(b);
+
+ AvahiService* service = reinterpret_cast<AvahiService*>(userdata);
+
+ switch (event) {
+ case AVAHI_BROWSER_FAILURE:
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Browser failure." << endl;
+ break;
+ }
+ case AVAHI_BROWSER_NEW:
+ {
+ if (!(avahi_service_resolver_new(service->listener, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, (AvahiLookupFlags)0, resolve_callback, userdata)))
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Failed to resolve the service." << endl;
+ break;
+ }
+ case AVAHI_BROWSER_REMOVE:
+ {
+ QString realname = QString(name);
+ realname.truncate(realname.length() - 5);
+
+ LocalBrowser::remove(bt::PeerID(realname.ascii()));
+
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Browser removed." << endl;
+ break;
+ }
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ ;
+ }
+ }
+
+ AvahiService::AvahiService(const bt::PeerID& id,bt::Uint16 port, const bt::SHA1Hash & infoHash)
+ : group(0), publisher_poll(0), listener_poll(0),publisher(0), listener(0), browser(0)
+ {
+ started = false;
+
+ this->id = id.toString();
+ this->port = port;
+ this->infoHash = infoHash.toString();
+ }
+
+ AvahiService::~AvahiService()
+ {
+ stop(0);
+ }
+
+ void AvahiService::stop(bt::WaitJob*)
+ {
+ if (started)
+ {
+ started = false;
+
+ publisher_poll = 0;
+ if (publisher)
+ {
+ avahi_client_free(publisher);
+ publisher = 0;
+ }
+
+ listener_poll = 0;
+ if (listener)
+ {
+ avahi_client_free(listener);
+ listener = 0;
+ }
+ }
+ }
+
+ void AvahiService::start()
+ {
+ started = startPublishing() && startBrowsing();
+ }
+
+ bool AvahiService::startPublishing()
+ {
+ group = NULL;
+ publisher_poll = NULL;
+ publisher = NULL;
+
+ if (!(publisher_poll = avahi_qt_poll_get()))
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Failed to create a poll for publishing." << endl;
+ stop();
+ return false;
+ }
+
+ publisher = avahi_client_new(publisher_poll, AVAHI_CLIENT_NO_FAIL, publisher_callback, this, NULL);
+
+ if (!(publisher))
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Failed to create a client for publishing." << endl;
+ stop();
+ return false;
+ }
+
+ return true;
+ }
+
+ bool AvahiService::startBrowsing()
+ {
+ listener_poll = NULL;
+ listener = NULL;
+ browser = NULL;
+
+ if (!(listener_poll = avahi_qt_poll_get()))
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Failed to create a poll for browsing." << endl;
+ stop();
+ return false;
+ }
+
+ listener = avahi_client_new(listener_poll,AVAHI_CLIENT_NO_FAIL, listener_callback, this, NULL);
+
+ if (!listener)
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Failed to create a client for browsing." << endl;
+ stop();
+ return false;
+ }
+
+ if (!(browser = avahi_service_browser_new(listener, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, avahi_strdup(QString("_" + infoHash + "._sub._bittorrent._tcp").ascii()), NULL, (AvahiLookupFlags)0, browser_callback, this)))
+ {
+ Out(SYS_ZCO|LOG_DEBUG) << "ZC: Failed to create a service browser." << endl;
+ stop();
+ return false;
+ }
+
+ return true;
+ }
+
+ void AvahiService::emitPeersReady()
+ {
+ peersReady(this);
+ }
+
+ void AvahiService::aboutToBeDestroyed()
+ {
+ serviceDestroyed(this);
+ }
+}
+
+#include "avahiservice.moc"
diff --git a/plugins/zeroconf/avahiservice.h b/plugins/zeroconf/avahiservice.h
new file mode 100644
index 0000000..10938d8
--- /dev/null
+++ b/plugins/zeroconf/avahiservice.h
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Lesly Weyts and Kevin Andre *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef AVAHI_SERVICE_HH
+#define AVAHI_SERVICE_HH
+
+/**
+ * @author Lesly Weyts and Kevin Andre
+ * @brief Handles everything directly related to Avahi
+ *
+ * This set of functions provide a simple way to use Avahi inside the KTorrent source code.
+ */
+
+#include <cstdlib>
+#include <qstring.h>
+
+
+#include <avahi-client/client.h>
+#include <avahi-client/publish.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/thread-watch.h>
+#include <avahi-common/malloc.h>
+
+#include <util/sha1hash.h>
+#include <interfaces/peersource.h>
+
+namespace kt
+{
+
+ class AvahiService : public kt::PeerSource
+ {
+ Q_OBJECT
+ public:
+ AvahiService(const bt::PeerID&, bt::Uint16, const bt::SHA1Hash&);
+ virtual ~AvahiService();
+
+ virtual void stop(bt::WaitJob* wjob = 0);
+ virtual void start();
+ virtual void aboutToBeDestroyed();
+
+ void emitPeersReady();
+
+ signals:
+ void serviceDestroyed(AvahiService* av);
+
+ private:
+ bool startPublishing();
+ bool startBrowsing();
+
+ friend void group_callback(AvahiEntryGroup*, AvahiEntryGroupState, void*);
+ friend void publish_service(AvahiService*, AvahiClient*);
+ friend void publisher_callback(AvahiClient*, AvahiClientState, void*);
+ friend void listener_callback(AvahiClient*, AvahiClientState, void*);
+
+ friend void resolve_callback(
+ AvahiServiceResolver*,
+ AvahiIfIndex,
+ AvahiProtocol,
+ AvahiResolverEvent,
+ const char*,
+ const char*,
+ const char*,
+ const char*,
+ const AvahiAddress*,
+ uint16_t,
+ AvahiStringList*,
+ AvahiLookupResultFlags,
+ void*
+ );
+
+ friend void browser_callback(
+ AvahiServiceBrowser*,
+ AvahiIfIndex,
+ AvahiProtocol,
+ AvahiBrowserEvent,
+ const char*,
+ const char*,
+ const char*,
+ AvahiLookupResultFlags,
+ void*
+ );
+
+ QString id;
+ int port;
+ QString infoHash;
+
+ bool started;
+
+ AvahiEntryGroup *group;
+ const AvahiPoll* publisher_poll;
+ const AvahiPoll* listener_poll;
+ AvahiClient* publisher;
+ AvahiClient* listener;
+ AvahiServiceBrowser *browser;
+ };
+}
+
+#endif
diff --git a/plugins/zeroconf/ktzeroconfplugin.desktop b/plugins/zeroconf/ktzeroconfplugin.desktop
new file mode 100644
index 0000000..caf68e1
--- /dev/null
+++ b/plugins/zeroconf/ktzeroconfplugin.desktop
@@ -0,0 +1,22 @@
+[Desktop Entry]
+Name=ZeroConfPlugin
+Name[bg]=Приставка ZeroConf
+Name[de]=ZeroConf-Modul
+Name[es]=Complemento de ZeroConf
+Name[et]=ZeroConfi plugin
+Name[it]=Plugin ZeroConf
+Name[nb]=ZeroConf-modul
+Name[nds]=ZeroConf-Moduul
+Name[pl]=Wtyczka ZeroConf
+Name[pt]='Plugin' do ZeroConf
+Name[pt_BR]=Plugin ZeroConf
+Name[sr]=ZeroConf прикључак
+Name[sr@Latn]=ZeroConf priključak
+Name[sv]=Zeroconf-insticksprogram
+Name[tr]=ZeroConf Eklentisi
+Name[uk]=Втулок ZeroConf
+Name[xx]=xxZeroConfPluginxx
+Name[zh_CN]=ZeroConf 插件
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktzeroconfplugin
diff --git a/plugins/zeroconf/localbrowser.cpp b/plugins/zeroconf/localbrowser.cpp
new file mode 100644
index 0000000..6306788
--- /dev/null
+++ b/plugins/zeroconf/localbrowser.cpp
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Lesly Weyts and Kevin Andre *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <list>
+#include <torrent/peerid.h>
+#include "localbrowser.h"
+
+
+namespace LocalBrowser
+{
+ static std::list<bt::PeerID> local_peers;
+
+ void remove(bt::PeerID id)
+ {
+ local_peers.remove(id);
+ }
+
+ void insert(bt::PeerID id)
+ {
+ if (!(check(id)))
+ local_peers.push_front(id);
+ }
+
+ bool check(bt::PeerID id)
+ {
+ for (std::list<bt::PeerID>::iterator i = local_peers.begin(); i != local_peers.end(); ++i) {
+ if (*i == id)
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/plugins/zeroconf/localbrowser.h b/plugins/zeroconf/localbrowser.h
new file mode 100644
index 0000000..0e06b99
--- /dev/null
+++ b/plugins/zeroconf/localbrowser.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Lesly Weyts and Kevin Andre *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef LOCAL_BROWSER_HHHH
+#define LOCAL_BROWSER_HHHH
+
+/**
+ * @author Lesly Weyts and Kevin Andre
+ * @brief Keep track of local peers
+ */
+
+
+
+namespace bt { class PeerID; }
+
+namespace LocalBrowser {
+
+ void remove(bt::PeerID);
+ void insert(bt::PeerID);
+ bool check (bt::PeerID);
+
+
+}
+#endif
diff --git a/plugins/zeroconf/zeroconfplugin.cpp b/plugins/zeroconf/zeroconfplugin.cpp
new file mode 100644
index 0000000..b906843
--- /dev/null
+++ b/plugins/zeroconf/zeroconfplugin.cpp
@@ -0,0 +1,135 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+#include <util/log.h>
+#include <interfaces/coreinterface.h>
+#include <interfaces/torrentinterface.h>
+#include <torrent/globals.h>
+#include <torrent/server.h>
+#include "zeroconfplugin.h"
+#include "avahiservice.h"
+
+
+#define NAME "Zeroconf"
+#define AUTHOR "Lesly Weyts and Kevin Andre"
+
+K_EXPORT_COMPONENT_FACTORY(ktzeroconfplugin,KGenericFactory<kt::ZeroConfPlugin>("ktzeroconfplugin"))
+
+using namespace bt;
+
+namespace kt
+{
+
+ ZeroConfPlugin::ZeroConfPlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name,args,NAME,i18n("Zeroconf"),AUTHOR,QString::null,i18n("Finds peers running ktorrent on the local network to share torrents with"),"ktplugins")
+ {
+ services.setAutoDelete(true);
+ }
+
+
+ ZeroConfPlugin::~ZeroConfPlugin()
+ {}
+
+ void ZeroConfPlugin::load()
+ {
+ CoreInterface* core = getCore();
+ connect(core,SIGNAL(torrentAdded( kt::TorrentInterface* )),
+ this,SLOT(torrentAdded( kt::TorrentInterface* )));
+ connect(core,SIGNAL(torrentRemoved( kt::TorrentInterface* )),
+ this,SLOT(torrentRemoved( kt::TorrentInterface* )));
+
+ // go over existing torrents and add them
+ bt::QueueManager* qman = core->getQueueManager();
+ for (QPtrList<kt::TorrentInterface>::iterator i = qman->begin();i != qman->end();i++)
+ {
+ torrentAdded(*i);
+ }
+ }
+
+ void ZeroConfPlugin::unload()
+ {
+ CoreInterface* core = getCore();
+ disconnect(core,SIGNAL(torrentAdded( kt::TorrentInterface* )),
+ this,SLOT(torrentAdded( kt::TorrentInterface* )));
+ disconnect(core,SIGNAL(torrentRemoved( kt::TorrentInterface* )),
+ this,SLOT(torrentRemoved( kt::TorrentInterface*)));
+
+ bt::PtrMap<kt::TorrentInterface*,AvahiService>::iterator i = services.begin();
+ while (i != services.end())
+ {
+ AvahiService* av = i->second;
+ kt::TorrentInterface* ti = i->first;
+ ti->removePeerSource(av);
+ i++;
+ }
+ services.clear();
+ }
+
+ void ZeroConfPlugin::torrentAdded(kt::TorrentInterface* tc)
+ {
+ if (services.contains(tc))
+ return;
+
+ bt::Uint16 port = bt::Globals::instance().getServer().getPortInUse();
+ AvahiService* av = new AvahiService(tc->getOwnPeerID(),port,tc->getInfoHash());
+ services.insert(tc,av);
+ tc->addPeerSource(av);
+ Out(SYS_ZCO|LOG_NOTICE) << "ZeroConf service added for "
+ << tc->getStats().torrent_name << endl;
+ connect(av,SIGNAL(serviceDestroyed( AvahiService* )),
+ this,SLOT(avahiServiceDestroyed( AvahiService* )));
+ }
+
+
+ void ZeroConfPlugin::torrentRemoved(kt::TorrentInterface* tc)
+ {
+ AvahiService* av = services.find(tc);
+ if (!av)
+ return;
+ Out(SYS_ZCO|LOG_NOTICE) << "ZeroConf service removed for "
+ << tc->getStats().torrent_name << endl;
+ tc->removePeerSource(av);
+ services.erase(tc);
+ }
+
+ void ZeroConfPlugin::avahiServiceDestroyed(AvahiService* av)
+ {
+ services.setAutoDelete(false);
+
+ Out(SYS_ZCO|LOG_NOTICE) << "ZeroConf service destroyed " << endl;
+ bt::PtrMap<kt::TorrentInterface*,AvahiService>::iterator i = services.begin();
+ while (i != services.end())
+ {
+ if (i->second == av)
+ {
+ services.erase(i->first);
+ break;
+ }
+ i++;
+ }
+ services.setAutoDelete(true);
+ }
+
+ bool ZeroConfPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+}
+#include "zeroconfplugin.moc"
diff --git a/plugins/zeroconf/zeroconfplugin.h b/plugins/zeroconf/zeroconfplugin.h
new file mode 100644
index 0000000..b34e851
--- /dev/null
+++ b/plugins/zeroconf/zeroconfplugin.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTZEROCONFPLUGIN_H
+#define KTZEROCONFPLUGIN_H
+
+#include <util/ptrmap.h>
+#include <interfaces/plugin.h>
+
+namespace kt
+{
+ class TorrentInterface;
+ class AvahiService;
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Plugin which handles the zeroconf service.
+ */
+ class ZeroConfPlugin : public Plugin
+ {
+ Q_OBJECT
+ public:
+ ZeroConfPlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~ZeroConfPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual bool versionCheck(const QString& version) const;
+
+ private slots:
+ /**
+ * A TorrentInterface was added
+ * @param tc
+ */
+ void torrentAdded(kt::TorrentInterface* tc);
+
+ /**
+ * A TorrentInterface was removed
+ * @param tc
+ */
+ void torrentRemoved(kt::TorrentInterface* tc);
+
+ /**
+ * An AvahiService has been destroyed by the psman
+ */
+ void avahiServiceDestroyed(AvahiService* av);
+
+ private:
+ bt::PtrMap<kt::TorrentInterface*,AvahiService> services;
+ };
+
+}
+
+#endif