You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

nntp.patch 237KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748
  1. From: Vsevolod Volkov <vvv@mutt.org.ua>
  2. Date: Sun, 16 Mar 2014 15:54:12 +0100
  3. Subject: nntp
  4. http://mutt.org.ua/download/mutt-1.5.23/patch-1.5.23.vvv.nntp.gz
  5. Signed-off-by: Matteo F. Vescovi <mfvescovi@gmail.com>
  6. Gbp-Pq: Topic mutt-patched
  7. ---
  8. ChangeLog.nntp | 399 +++++++++
  9. Makefile.am | 2 +
  10. OPS | 23 +-
  11. account.c | 27 +
  12. account.h | 3 +-
  13. attach.h | 2 +-
  14. browser.c | 470 +++++++++-
  15. browser.h | 7 +
  16. buffy.c | 12 +
  17. complete.c | 64 ++
  18. compose.c | 177 +++-
  19. configure.ac | 11 +-
  20. curs_main.c | 312 ++++++-
  21. doc/Muttrc | 300 +++++++
  22. doc/manual.xml.head | 20 +
  23. doc/mutt.man | 8 +-
  24. functions.h | 59 +-
  25. globals.h | 16 +
  26. hash.c | 27 +
  27. hash.h | 3 +-
  28. hcache.c | 12 +
  29. hdrline.c | 25 +
  30. headers.c | 3 +
  31. init.c | 22 +
  32. init.h | 217 +++++
  33. keymap.c | 1 -
  34. mailbox.h | 3 +
  35. main.c | 54 ++
  36. mutt.h | 39 +-
  37. mutt_sasl.c | 5 +
  38. muttlib.c | 17 +-
  39. mx.c | 75 ++
  40. mx.h | 3 +
  41. newsrc.c | 1260 +++++++++++++++++++++++++++
  42. nntp.c | 2404 +++++++++++++++++++++++++++++++++++++++++++++++++++
  43. nntp.h | 167 ++++
  44. pager.c | 81 +-
  45. parse.c | 48 +-
  46. pattern.c | 8 +
  47. po/POTFILES.in | 2 +
  48. postpone.c | 11 +
  49. protos.h | 1 +
  50. recvattach.c | 34 +-
  51. recvcmd.c | 61 +-
  52. send.c | 163 +++-
  53. sendlib.c | 78 +-
  54. sort.c | 16 +
  55. url.c | 4 +-
  56. url.h | 2 +
  57. 49 files changed, 6674 insertions(+), 84 deletions(-)
  58. create mode 100644 ChangeLog.nntp
  59. create mode 100644 newsrc.c
  60. create mode 100644 nntp.c
  61. create mode 100644 nntp.h
  62. diff --git a/ChangeLog.nntp b/ChangeLog.nntp
  63. new file mode 100644
  64. index 0000000..70d1126
  65. --- /dev/null
  66. +++ b/ChangeLog.nntp
  67. @@ -0,0 +1,399 @@
  68. +* Thu Mar 13 2014 Vsevolod Volkov <vvv@mutt.org.ua>
  69. +- update to 1.5.23
  70. +
  71. +* Tue Oct 29 2013 Vsevolod Volkov <vvv@mutt.org.ua>
  72. +- minor bug fixed while removing new articles
  73. +
  74. +* Fri Oct 18 2013 Vsevolod Volkov <vvv@mutt.org.ua>
  75. +- update to 1.5.22
  76. +
  77. +* Tue Nov 27 2012 Vsevolod Volkov <vvv@mutt.org.ua>
  78. +- SASL authentication
  79. +- new option nntp_authenticators
  80. +
  81. +* Fri Nov 16 2012 Vsevolod Volkov <vvv@mutt.org.ua>
  82. +- support of NNTP commands: CAPABILITIES, STARTTLS, LIST NEWSGROUPS,
  83. + LIST OVERVIEW.FMT, OVER, DATE
  84. +- added bcache support
  85. +- newss URI scheme renamed to snews
  86. +- removed option nntp_reconnect
  87. +
  88. +* Sun Sep 16 2012 Vsevolod Volkov <vvv@mutt.org.ua>
  89. +- internal header caching replaced with hcache
  90. +- new option newsgroups_charset
  91. +
  92. +* Wed Sep 16 2010 Vsevolod Volkov <vvv@mutt.org.ua>
  93. +- update to 1.5.21
  94. +
  95. +* Thu Aug 13 2009 Vsevolod Volkov <vvv@mutt.org.ua>
  96. +- fixed writting references in nntp_save_cache_group()
  97. +
  98. +* Tue Jun 15 2009 Vsevolod Volkov <vvv@mutt.org.ua>
  99. +- update to 1.5.20
  100. +
  101. +* Tue Mar 20 2009 Vsevolod Volkov <vvv@mutt.org.ua>
  102. +- save Date: header of recorded outgoing articles
  103. +
  104. +* Tue Jan 6 2009 Vsevolod Volkov <vvv@mutt.org.ua>
  105. +- update to 1.5.19
  106. +
  107. +* Mon May 19 2008 Vsevolod Volkov <vvv@mutt.org.ua>
  108. +- update to 1.5.18
  109. +- fixed SIGSEGV when followup or forward to newsgroup
  110. +
  111. +* Sun Nov 4 2007 Vsevolod Volkov <vvv@mutt.org.ua>
  112. +- update to 1.5.17
  113. +
  114. +* Tue Jul 3 2007 Vsevolod Volkov <vvv@mutt.org.ua>
  115. +- fixed arguments of nntp_format_str()
  116. +
  117. +* Fri Jun 15 2007 Vsevolod Volkov <vvv@mutt.org.ua>
  118. +- fixed error selecting news group
  119. +
  120. +* Tue Jun 12 2007 Vsevolod Volkov <vvv@mutt.org.ua>
  121. +- update to 1.5.16
  122. +
  123. +* Wed Apr 11 2007 Vsevolod Volkov <vvv@mutt.org.ua>
  124. +- fixed posting error if $smtp_url is set
  125. +- added support of print-style sequence %R (x-comment-to)
  126. +
  127. +* Sun Apr 8 2007 Vsevolod Volkov <vvv@mutt.org.ua>
  128. +- update to 1.5.15
  129. +- nntp://... url changed to news://...
  130. +- added indicator of fetching descriptions progress
  131. +
  132. +* Tue Feb 28 2007 Vsevolod Volkov <vvv@mutt.org.ua>
  133. +- update to 1.5.14
  134. +
  135. +* Tue Aug 15 2006 Vsevolod Volkov <vvv@mutt.org.ua>
  136. +- update to 1.5.13
  137. +
  138. +* Mon Jul 17 2006 Vsevolod Volkov <vvv@mutt.org.ua>
  139. +- update to 1.5.12
  140. +- fixed reading empty .newsrc
  141. +
  142. +* Sat Sep 17 2005 Vsevolod Volkov <vvv@mutt.org.ua>
  143. +- update to 1.5.11
  144. +
  145. +* Sat Aug 13 2005 Vsevolod Volkov <vvv@mutt.org.ua>
  146. +- update to 1.5.10
  147. +
  148. +* Sun Mar 13 2005 Vsevolod Volkov <vvv@mutt.org.ua>
  149. +- update to 1.5.9
  150. +
  151. +* Sun Feb 13 2005 Vsevolod Volkov <vvv@mutt.org.ua>
  152. +- update to 1.5.8
  153. +
  154. +* Sat Feb 5 2005 Vsevolod Volkov <vvv@mutt.org.ua>
  155. +- update to 1.5.7
  156. +- function mutt_update_list_file() moved to newsrc.c and changed algorithm
  157. +
  158. +* Thu Jul 8 2004 Vsevolod Volkov <vvv@mutt.org.ua>
  159. +- fixed error in nntp_logout_all()
  160. +
  161. +* Sat Apr 3 2004 Vsevolod Volkov <vvv@mutt.org.ua>
  162. +- fixed debug output in mutt_newsrc_update()
  163. +- added optional support of LISTGROUP command
  164. +- fixed typo in nntp_parse_xref()
  165. +
  166. +* Tue Feb 3 2004 Vsevolod Volkov <vvv@mutt.org.ua>
  167. +- update to 1.5.6
  168. +
  169. +* Thu Dec 18 2003 Vsevolod Volkov <vvv@mutt.org.ua>
  170. +- fixed compose menu
  171. +
  172. +* Thu Nov 6 2003 Vsevolod Volkov <vvv@mutt.org.ua>
  173. +- update to 1.5.5.1
  174. +
  175. +* Wed Nov 5 2003 Vsevolod Volkov <vvv@mutt.org.ua>
  176. +- update to 1.5.5
  177. +- added space after newsgroup name in .newsrc file
  178. +
  179. +* Sun May 18 2003 Vsevolod Volkov <vvv@mutt.org.ua>
  180. +- nntp patch: fixed SIGSEGV when posting article
  181. +
  182. +* Sat Mar 22 2003 Vsevolod Volkov <vvv@mutt.org.ua>
  183. +- update to 1.5.4
  184. +
  185. +* Sat Dec 21 2002 Vsevolod Volkov <vvv@mutt.org.ua>
  186. +- update to 1.5.3
  187. +- replace safe_free calls by the FREE macro
  188. +
  189. +* Fri Dec 6 2002 Vsevolod Volkov <vvv@mutt.org.ua>
  190. +- update to 1.5.2
  191. +- nntp authentication can be passed after any command
  192. +
  193. +* Sat May 4 2002 Vsevolod Volkov <vvv@mutt.org.ua>
  194. +- update to 1.5.1
  195. +
  196. +* Thu May 2 2002 Vsevolod Volkov <vvv@mutt.org.ua>
  197. +- update to 1.3.99
  198. +
  199. +* Wed Mar 13 2002 Vsevolod Volkov <vvv@mutt.org.ua>
  200. +- update to 1.3.28
  201. +- fixed SIGSEGV in <get-message>, <get-parent>, <get-children>,
  202. + <reconstruct-thread> functions
  203. +- fixed message about nntp reconnect
  204. +- fixed <attach-news-message> function using browser
  205. +- added support of Followup-To: poster
  206. +- added %n (new articles) in group_index_format
  207. +- posting articles without inews by default
  208. +
  209. +* Wed Jan 23 2002 Vsevolod Volkov <vvv@mutt.org.ua>
  210. +- update to 1.3.27
  211. +
  212. +* Fri Jan 18 2002 Vsevolod Volkov <vvv@mutt.org.ua>
  213. +- update to 1.3.26
  214. +
  215. +* Thu Jan 3 2002 Vsevolod Volkov <vvv@mutt.org.ua>
  216. +- update to 1.3.25
  217. +- accelerated speed of access to news->newsgroups hash (by <gul@gul.kiev.ua>)
  218. +- added default content disposition
  219. +
  220. +* Mon Dec 3 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  221. +- update to 1.3.24
  222. +
  223. +* Fri Nov 9 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  224. +- update to 1.3.23.2
  225. +- fixed segfault if mutt_conn_find() returns null
  226. +
  227. +* Wed Oct 31 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  228. +- update to 1.3.23.1
  229. +- added support of LISTGROUP command
  230. +- added support for servers with broken overview
  231. +- disabled <flag-message> function on news server
  232. +- fixed error storing bad authentication information
  233. +
  234. +* Wed Oct 10 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  235. +- update to 1.3.23
  236. +- fixed typo in buffy.c
  237. +- added substitution of %s parameter in $inews variable
  238. +
  239. +* Fri Aug 31 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  240. +- update to 1.3.22.1
  241. +- update to 1.3.22
  242. +
  243. +* Thu Aug 23 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  244. +- update to 1.3.21
  245. +
  246. +* Wed Jul 25 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  247. +- update to 1.3.20
  248. +- removed 'server-hook', use 'account-hook' instead
  249. +- fixed error opening NNTP server without newsgroup using -f option
  250. +
  251. +* Fri Jun 8 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  252. +- update to 1.3.19
  253. +
  254. +* Sat May 5 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  255. +- update to 1.3.18
  256. +- fixed typo in nntp_attempt_features()
  257. +- changed algorithm of XGTITLE command testing
  258. +- disabled writing of NNTP password in debug file
  259. +- fixed reading and writing of long newsrc lines
  260. +- changed checking of last line while reading lines from server
  261. +- fixed possible buffer overrun in nntp_parse_newsrc_line()
  262. +- removed checking of XHDR command
  263. +- compare NNTP return codes without trailing space
  264. +
  265. +* Thu Mar 29 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  266. +- update to 1.3.17
  267. +- support for 'LIST NEWSGROUPS' command to read descriptions
  268. +
  269. +* Fri Mar 2 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  270. +- update to 1.3.16
  271. +
  272. +* Wed Feb 14 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  273. +- update to 1.3.15
  274. +
  275. +* Sun Jan 28 2001 Vsevolod Volkov <vvv@mutt.org.ua>
  276. +- update to 1.3.14
  277. +- show number of tagged messages patch from Felix von Leitner <leitner@fefe.de>
  278. +
  279. +* Sun Dec 31 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  280. +- update to 1.3.13
  281. +
  282. +* Sat Dec 30 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  283. +- Fixed problem if last article in group is deleted
  284. +
  285. +* Fri Dec 22 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  286. +- Fixed checking of XGTITLE command on some servers
  287. +
  288. +* Mon Dec 18 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  289. +- Added \r in AUTHINFO commands
  290. +
  291. +* Mon Nov 27 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  292. +- update to 1.3.12
  293. +
  294. +* Wed Nov 1 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  295. +- update to 1.3.11
  296. +- fixed error opening newsgroup from mutt started with -g or -G
  297. +
  298. +* Thu Oct 12 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  299. +- update to 1.3.10
  300. +- hotkey 'G' (get-message) replaced with '^G'
  301. +
  302. +* Thu Sep 21 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  303. +- update to 1.3.9
  304. +- changed delay displaying error messages from 1 to 2 seconds
  305. +- fixed error compiling with nntp and without imap
  306. +
  307. +* Wed Sep 6 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  308. +- fixed catchup in index
  309. +- fixed nntp_open_mailbox()
  310. +
  311. +* Sat Sep 2 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  312. +- functions <edit> and <delete-entry> disabled
  313. +- format of news mailbox names changed to url form
  314. +- option nntp_attempts removed
  315. +- option reconnect_news renamed to nntp_reconnect
  316. +- default value of nntp_poll changed from 30 to 60
  317. +- error handling improved
  318. +
  319. +* Wed Aug 30 2000 Vsevolod Volkov <vvv@mutt.org.ua>
  320. +- update to 1.3.8
  321. +- new option show_only_unread
  322. +- add newsgroup completion
  323. +
  324. +* Fri Aug 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  325. +- update to 1.3.7
  326. +
  327. +* Sat Jul 29 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  328. +- update to 1.3.6
  329. +
  330. +* Sun Jul 9 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  331. +- update to 1.3.5
  332. +- authentication code update
  333. +- fix for changing to newsgroup from mailbox with read messages
  334. +- socket code optimization
  335. +
  336. +* Wed Jun 21 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  337. +- update to 1.3.4
  338. +
  339. +* Wed Jun 14 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  340. +- don't substitute current newsgroup with deleted new messages
  341. +
  342. +* Mon Jun 12 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  343. +- update to 1.3.3
  344. +- fix for substitution of newsgroup after reconnection
  345. +- fix for loading newsgroups with very long names
  346. +- fix for loading more than 32768 newsgroups
  347. +
  348. +* Wed May 24 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  349. +- update to 1.3.2
  350. +
  351. +* Sat May 20 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  352. +- update to 1.3.1
  353. +
  354. +* Fri May 12 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  355. +- update to 1.3
  356. +
  357. +* Thu May 11 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  358. +- update to 1.2
  359. +
  360. +* Thu May 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  361. +- update to 1.1.14
  362. +
  363. +* Sun Apr 23 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  364. +- update to 1.1.12
  365. +
  366. +* Fri Apr 7 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  367. +- add substitution of newsgroup with new messages by default
  368. +
  369. +* Wed Apr 5 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  370. +- add attach message from newsgroup
  371. +- add one-line help in newsreader mode
  372. +- disable 'change-dir' command in newsgroups browser
  373. +- add -G option
  374. +
  375. +* Tue Apr 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  376. +- get default news server name from file /etc/nntpserver
  377. +- use case insensitive server names
  378. +- add print-style sequence %s to $newsrc
  379. +- add -g option
  380. +
  381. +* Sat Apr 1 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  382. +- remove 'X-FTN-Origin' header processing
  383. +
  384. +* Thu Mar 30 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  385. +- update to 1.1.11
  386. +- update to 1.1.10
  387. +
  388. +* Thu Mar 23 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  389. +- fix mutt_select_newsserver()
  390. +- remove 'toggle-mode' function
  391. +- add 'change-newsgroup' function
  392. +
  393. +* Wed Mar 22 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  394. +- fix server-hook
  395. +
  396. +* Tue Mar 21 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  397. +- fix error 'bounce' function after 'post'
  398. +- add 'forward to newsgroup' function
  399. +
  400. +* Mon Mar 20 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  401. +- 'forward' function works in newsreader mode
  402. +- add 'post' and 'followup' functions to pager and attachment menu
  403. +- fix active descriptions and allowed flag reload
  404. +
  405. +* Tue Mar 14 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  406. +- update to 1.1.9
  407. +- remove deleted newsgroups from list
  408. +
  409. +* Mon Mar 13 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  410. +- update .newsrc in browser
  411. +
  412. +* Sun Mar 12 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  413. +- reload .newsrc if externally modified
  414. +- fix active cache update
  415. +
  416. +* Sun Mar 5 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  417. +- update to 1.1.8
  418. +
  419. +* Sat Mar 4 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  420. +- patch *.update_list_file is not required
  421. +- count lines when loading descriptions
  422. +- remove cache of unsubscribed newsgroups
  423. +
  424. +* Thu Mar 2 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  425. +- load list of newsgroups from cache faster
  426. +
  427. +* Wed Mar 1 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  428. +- update to 1.1.7
  429. +
  430. +* Tue Feb 29 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  431. +- fix unread messages in browser
  432. +- fix newsrc_gen_entries()
  433. +
  434. +* Mon Feb 28 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  435. +- fix mutt_newsgroup_stat()
  436. +- fix nntp_delete_cache()
  437. +- fix nntp_get_status()
  438. +- fix check_children()
  439. +- fix nntp_fetch_headers()
  440. +
  441. +* Fri Feb 25 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  442. +- update to 1.1.5
  443. +
  444. +* Thu Feb 24 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  445. +- fix updating new messages in cache
  446. +
  447. +* Mon Feb 21 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  448. +- change default cache filenames
  449. +- fix updating new messages in cache
  450. +
  451. +* Fri Feb 18 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  452. +- fix segmentation fault in news groups browser
  453. +
  454. +* Tue Feb 15 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  455. +- update to 1.1.4
  456. +
  457. +* Thu Feb 10 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  458. +- update to 1.1.3
  459. +
  460. +* Sun Jan 30 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  461. +- add X-Comment-To editing
  462. +- add my_hdr support for Newsgroups:, Followup-To: and X-Comment-To: headers
  463. +- add variables $ask_followup_to and $ask_x_comment_to
  464. +
  465. +* Fri Jan 28 2000 Vsevolod Volkov <vvv@mutt.kiev.ua>
  466. +- update to 1.1.2
  467. diff --git a/Makefile.am b/Makefile.am
  468. index 2fc6b1d..6d56156 100644
  469. --- a/Makefile.am
  470. +++ b/Makefile.am
  471. @@ -58,6 +58,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c crypt-gpgme.c crypt-mod-pgp-classic.c \
  472. mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \
  473. mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \
  474. pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \
  475. + nntp.c newsrc.c \
  476. smime.c smtp.c utf8.c wcwidth.c \
  477. bcache.h browser.h hcache.h mbyte.h mutt_idna.h remailer.h url.h
  478. @@ -69,6 +70,7 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP OPS.CRYPT OPS.SMIME TODO UPDATING \
  479. mutt_regex.h mutt_sasl.h mutt_socket.h mutt_ssl.h mutt_tunnel.h \
  480. mx.h pager.h pgp.h pop.h protos.h rfc1524.h rfc2047.h \
  481. rfc2231.h rfc822.h rfc3676.h sha1.h sort.h mime.types VERSION prepare \
  482. + nntp.h ChangeLog.nntp \
  483. _regex.h OPS.MIX README.SECURITY remailer.c remailer.h browser.h \
  484. mbyte.h lib.h extlib.c pgpewrap.c smime_keys.pl pgplib.h \
  485. README.SSL smime.h group.h \
  486. diff --git a/OPS b/OPS
  487. index 1ed9c96..b900373 100644
  488. --- a/OPS
  489. +++ b/OPS
  490. @@ -8,14 +8,16 @@ OP_BOUNCE_MESSAGE "remail a message to another user"
  491. OP_BROWSER_NEW_FILE "select a new file in this directory"
  492. OP_BROWSER_VIEW_FILE "view file"
  493. OP_BROWSER_TELL "display the currently selected file's name"
  494. -OP_BROWSER_SUBSCRIBE "subscribe to current mailbox (IMAP only)"
  495. -OP_BROWSER_UNSUBSCRIBE "unsubscribe from current mailbox (IMAP only)"
  496. +OP_BROWSER_SUBSCRIBE "subscribe to current mbox (IMAP/NNTP only)"
  497. +OP_BROWSER_UNSUBSCRIBE "unsubscribe from current mbox (IMAP/NNTP only)"
  498. OP_BROWSER_TOGGLE_LSUB "toggle view all/subscribed mailboxes (IMAP only)"
  499. OP_BUFFY_LIST "list mailboxes with new mail"
  500. +OP_CATCHUP "mark all articles in newsgroup as read"
  501. OP_CHANGE_DIRECTORY "change directories"
  502. OP_CHECK_NEW "check mailboxes for new mail"
  503. OP_COMPOSE_ATTACH_FILE "attach file(s) to this message"
  504. OP_COMPOSE_ATTACH_MESSAGE "attach message(s) to this message"
  505. +OP_COMPOSE_ATTACH_NEWS_MESSAGE "attach news article(s) to this message"
  506. OP_COMPOSE_EDIT_BCC "edit the BCC list"
  507. OP_COMPOSE_EDIT_CC "edit the CC list"
  508. OP_COMPOSE_EDIT_DESCRIPTION "edit attachment description"
  509. @@ -26,7 +28,10 @@ OP_COMPOSE_EDIT_FROM "edit the from field"
  510. OP_COMPOSE_EDIT_HEADERS "edit the message with headers"
  511. OP_COMPOSE_EDIT_MESSAGE "edit the message"
  512. OP_COMPOSE_EDIT_MIME "edit attachment using mailcap entry"
  513. +OP_COMPOSE_EDIT_NEWSGROUPS "edit the newsgroups list"
  514. OP_COMPOSE_EDIT_REPLY_TO "edit the Reply-To field"
  515. +OP_COMPOSE_EDIT_FOLLOWUP_TO "edit the Followup-To field"
  516. +OP_COMPOSE_EDIT_X_COMMENT_TO "edit the X-Comment-To field"
  517. OP_COMPOSE_EDIT_SUBJECT "edit the subject of this message"
  518. OP_COMPOSE_EDIT_TO "edit the TO list"
  519. OP_CREATE_MAILBOX "create a new mailbox (IMAP only)"
  520. @@ -85,8 +90,13 @@ OP_EXIT "exit this menu"
  521. OP_FILTER "filter attachment through a shell command"
  522. OP_FIRST_ENTRY "move to the first entry"
  523. OP_FLAG_MESSAGE "toggle a message's 'important' flag"
  524. +OP_FOLLOWUP "followup to newsgroup"
  525. +OP_FORWARD_TO_GROUP "forward to newsgroup"
  526. OP_FORWARD_MESSAGE "forward a message with comments"
  527. OP_GENERIC_SELECT_ENTRY "select the current entry"
  528. +OP_GET_CHILDREN "get all children of the current message"
  529. +OP_GET_MESSAGE "get message with Message-Id"
  530. +OP_GET_PARENT "get parent of the current message"
  531. OP_GROUP_REPLY "reply to all recipients"
  532. OP_HALF_DOWN "scroll down 1/2 page"
  533. OP_HALF_UP "scroll up 1/2 page"
  534. @@ -94,11 +104,14 @@ OP_HELP "this screen"
  535. OP_JUMP "jump to an index number"
  536. OP_LAST_ENTRY "move to the last entry"
  537. OP_LIST_REPLY "reply to specified mailing list"
  538. +OP_LOAD_ACTIVE "load list of all newsgroups from NNTP server"
  539. OP_MACRO "execute a macro"
  540. OP_MAIL "compose a new mail message"
  541. OP_MAIN_BREAK_THREAD "break the thread in two"
  542. OP_MAIN_CHANGE_FOLDER "open a different folder"
  543. OP_MAIN_CHANGE_FOLDER_READONLY "open a different folder in read only mode"
  544. +OP_MAIN_CHANGE_GROUP "open a different newsgroup"
  545. +OP_MAIN_CHANGE_GROUP_READONLY "open a different newsgroup in read only mode"
  546. OP_MAIN_CLEAR_FLAG "clear a status flag from a message"
  547. OP_MAIN_DELETE_PATTERN "delete messages matching a pattern"
  548. OP_MAIN_IMAP_FETCH "force retrieval of mail from IMAP server"
  549. @@ -138,6 +151,7 @@ OP_PAGER_HIDE_QUOTED "toggle display of quoted text"
  550. OP_PAGER_SKIP_QUOTED "skip beyond quoted text"
  551. OP_PAGER_TOP "jump to the top of the message"
  552. OP_PIPE "pipe message/attachment to a shell command"
  553. +OP_POST "post message to newsgroup"
  554. OP_PREV_ENTRY "move to the previous entry"
  555. OP_PREV_LINE "scroll up one line"
  556. OP_PREV_PAGE "move to the previous page"
  557. @@ -147,6 +161,7 @@ OP_QUERY "query external program for addresses"
  558. OP_QUERY_APPEND "append new query results to current results"
  559. OP_QUIT "save changes to mailbox and quit"
  560. OP_RECALL_MESSAGE "recall a postponed message"
  561. +OP_RECONSTRUCT_THREAD "reconstruct thread containing current message"
  562. OP_REDRAW "clear and redraw the screen"
  563. OP_REFORMAT_WINCH "{internal}"
  564. OP_RENAME_MAILBOX "rename the current mailbox (IMAP only)"
  565. @@ -161,18 +176,22 @@ OP_SEARCH_TOGGLE "toggle search pattern coloring"
  566. OP_SHELL_ESCAPE "invoke a command in a subshell"
  567. OP_SORT "sort messages"
  568. OP_SORT_REVERSE "sort messages in reverse order"
  569. +OP_SUBSCRIBE_PATTERN "subscribe to newsgroups matching a pattern"
  570. OP_TAG "tag the current entry"
  571. OP_TAG_PREFIX "apply next function to tagged messages"
  572. OP_TAG_PREFIX_COND "apply next function ONLY to tagged messages"
  573. OP_TAG_SUBTHREAD "tag the current subthread"
  574. OP_TAG_THREAD "tag the current thread"
  575. OP_TOGGLE_NEW "toggle a message's 'new' flag"
  576. +OP_TOGGLE_READ "toggle view of read messages"
  577. OP_TOGGLE_WRITE "toggle whether the mailbox will be rewritten"
  578. OP_TOGGLE_MAILBOXES "toggle whether to browse mailboxes or all files"
  579. OP_TOP_PAGE "move to the top of the page"
  580. +OP_UNCATCHUP "mark all articles in newsgroup as unread"
  581. OP_UNDELETE "undelete the current entry"
  582. OP_UNDELETE_THREAD "undelete all messages in thread"
  583. OP_UNDELETE_SUBTHREAD "undelete all messages in subthread"
  584. +OP_UNSUBSCRIBE_PATTERN "unsubscribe from newsgroups matching a pattern"
  585. OP_VERSION "show the Mutt version number and date"
  586. OP_VIEW_ATTACH "view attachment using mailcap entry if necessary"
  587. OP_VIEW_ATTACHMENTS "show MIME attachments"
  588. diff --git a/account.c b/account.c
  589. index bf59995..624f931 100644
  590. --- a/account.c
  591. +++ b/account.c
  592. @@ -51,8 +51,17 @@ int mutt_account_match (const ACCOUNT* a1, const ACCOUNT* a2)
  593. user = PopUser;
  594. #endif
  595. +#ifdef USE_NNTP
  596. + if (a1->type == M_ACCT_TYPE_NNTP && NntpUser)
  597. + user = NntpUser;
  598. +#endif
  599. +
  600. if (a1->flags & a2->flags & M_ACCT_USER)
  601. return (!strcmp (a1->user, a2->user));
  602. +#ifdef USE_NNTP
  603. + if (a1->type == M_ACCT_TYPE_NNTP)
  604. + return a1->flags & M_ACCT_USER && a1->user[0] ? 0 : 1;
  605. +#endif
  606. if (a1->flags & M_ACCT_USER)
  607. return (!strcmp (a1->user, user));
  608. if (a2->flags & M_ACCT_USER)
  609. @@ -130,6 +139,16 @@ void mutt_account_tourl (ACCOUNT* account, ciss_url_t* url)
  610. }
  611. #endif
  612. +#ifdef USE_NNTP
  613. + if (account->type == M_ACCT_TYPE_NNTP)
  614. + {
  615. + if (account->flags & M_ACCT_SSL)
  616. + url->scheme = U_NNTPS;
  617. + else
  618. + url->scheme = U_NNTP;
  619. + }
  620. +#endif
  621. +
  622. url->host = account->host;
  623. if (account->flags & M_ACCT_PORT)
  624. url->port = account->port;
  625. @@ -155,6 +174,10 @@ int mutt_account_getuser (ACCOUNT* account)
  626. else if ((account->type == M_ACCT_TYPE_POP) && PopUser)
  627. strfcpy (account->user, PopUser, sizeof (account->user));
  628. #endif
  629. +#ifdef USE_NNTP
  630. + else if ((account->type == M_ACCT_TYPE_NNTP) && NntpUser)
  631. + strfcpy (account->user, NntpUser, sizeof (account->user));
  632. +#endif
  633. else if (option (OPTNOCURSES))
  634. return -1;
  635. /* prompt (defaults to unix username), copy into account->user */
  636. @@ -217,6 +240,10 @@ int mutt_account_getpass (ACCOUNT* account)
  637. else if ((account->type == M_ACCT_TYPE_SMTP) && SmtpPass)
  638. strfcpy (account->pass, SmtpPass, sizeof (account->pass));
  639. #endif
  640. +#ifdef USE_NNTP
  641. + else if ((account->type == M_ACCT_TYPE_NNTP) && NntpPass)
  642. + strfcpy (account->pass, NntpPass, sizeof (account->pass));
  643. +#endif
  644. else if (option (OPTNOCURSES))
  645. return -1;
  646. else
  647. diff --git a/account.h b/account.h
  648. index 662fb32..9febee6 100644
  649. --- a/account.h
  650. +++ b/account.h
  651. @@ -29,7 +29,8 @@ enum
  652. M_ACCT_TYPE_NONE = 0,
  653. M_ACCT_TYPE_IMAP,
  654. M_ACCT_TYPE_POP,
  655. - M_ACCT_TYPE_SMTP
  656. + M_ACCT_TYPE_SMTP,
  657. + M_ACCT_TYPE_NNTP
  658. };
  659. /* account flags */
  660. diff --git a/attach.h b/attach.h
  661. index 928408a..071f22c 100644
  662. --- a/attach.h
  663. +++ b/attach.h
  664. @@ -50,7 +50,7 @@ void mutt_print_attachment_list (FILE *fp, int tag, BODY *top);
  665. void mutt_attach_bounce (FILE *, HEADER *, ATTACHPTR **, short, BODY *);
  666. void mutt_attach_resend (FILE *, HEADER *, ATTACHPTR **, short, BODY *);
  667. -void mutt_attach_forward (FILE *, HEADER *, ATTACHPTR **, short, BODY *);
  668. +void mutt_attach_forward (FILE *, HEADER *, ATTACHPTR **, short, BODY *, int);
  669. void mutt_attach_reply (FILE *, HEADER *, ATTACHPTR **, short, BODY *, int);
  670. #endif /* _ATTACH_H_ */
  671. diff --git a/browser.c b/browser.c
  672. index dbb31c8..3c84044 100644
  673. --- a/browser.c
  674. +++ b/browser.c
  675. @@ -32,6 +32,9 @@
  676. #ifdef USE_IMAP
  677. #include "imap.h"
  678. #endif
  679. +#ifdef USE_NNTP
  680. +#include "nntp.h"
  681. +#endif
  682. #include <stdlib.h>
  683. #include <dirent.h>
  684. @@ -50,6 +53,19 @@ static const struct mapping_t FolderHelp[] = {
  685. { NULL, 0 }
  686. };
  687. +#ifdef USE_NNTP
  688. +static struct mapping_t FolderNewsHelp[] = {
  689. + { N_("Exit"), OP_EXIT },
  690. + { N_("List"), OP_TOGGLE_MAILBOXES },
  691. + { N_("Subscribe"), OP_BROWSER_SUBSCRIBE },
  692. + { N_("Unsubscribe"), OP_BROWSER_UNSUBSCRIBE },
  693. + { N_("Catchup"), OP_CATCHUP },
  694. + { N_("Mask"), OP_ENTER_MASK },
  695. + { N_("Help"), OP_HELP },
  696. + { NULL, 0 }
  697. +};
  698. +#endif
  699. +
  700. typedef struct folder_t
  701. {
  702. struct folder_file *ff;
  703. @@ -116,9 +132,17 @@ static void browser_sort (struct browser_state *state)
  704. case SORT_ORDER:
  705. return;
  706. case SORT_DATE:
  707. +#ifdef USE_NNTP
  708. + if (option (OPTNEWS))
  709. + return;
  710. +#endif
  711. f = browser_compare_date;
  712. break;
  713. case SORT_SIZE:
  714. +#ifdef USE_NNTP
  715. + if (option (OPTNEWS))
  716. + return;
  717. +#endif
  718. f = browser_compare_size;
  719. break;
  720. case SORT_SUBJECT:
  721. @@ -325,8 +349,111 @@ folder_format_str (char *dest, size_t destlen, size_t col, char op, const char *
  722. return (src);
  723. }
  724. +#ifdef USE_NNTP
  725. +static const char *
  726. +newsgroup_format_str (char *dest, size_t destlen, size_t col, char op, const char *src,
  727. + const char *fmt, const char *ifstring, const char *elsestring,
  728. + unsigned long data, format_flag flags)
  729. +{
  730. + char fn[SHORT_STRING], tmp[SHORT_STRING];
  731. + FOLDER *folder = (FOLDER *) data;
  732. +
  733. + switch (op)
  734. + {
  735. + case 'C':
  736. + snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
  737. + snprintf (dest, destlen, tmp, folder->num + 1);
  738. + break;
  739. +
  740. + case 'f':
  741. + strncpy (fn, folder->ff->name, sizeof(fn) - 1);
  742. + snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
  743. + snprintf (dest, destlen, tmp, fn);
  744. + break;
  745. +
  746. + case 'N':
  747. + snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
  748. + if (folder->ff->nd->subscribed)
  749. + snprintf (dest, destlen, tmp, ' ');
  750. + else
  751. + snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : 'u');
  752. + break;
  753. +
  754. + case 'M':
  755. + snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
  756. + if (folder->ff->nd->deleted)
  757. + snprintf (dest, destlen, tmp, 'D');
  758. + else
  759. + snprintf (dest, destlen, tmp, folder->ff->nd->allowed ? ' ' : '-');
  760. + break;
  761. +
  762. + case 's':
  763. + if (flags & M_FORMAT_OPTIONAL)
  764. + {
  765. + if (folder->ff->nd->unread != 0)
  766. + mutt_FormatString (dest, destlen, col, ifstring, newsgroup_format_str,
  767. + data, flags);
  768. + else
  769. + mutt_FormatString (dest, destlen, col, elsestring, newsgroup_format_str,
  770. + data, flags);
  771. + }
  772. + else if (Context && Context->data == folder->ff->nd)
  773. + {
  774. + snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
  775. + snprintf (dest, destlen, tmp, Context->unread);
  776. + }
  777. + else
  778. + {
  779. + snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
  780. + snprintf (dest, destlen, tmp, folder->ff->nd->unread);
  781. + }
  782. + break;
  783. +
  784. + case 'n':
  785. + if (Context && Context->data == folder->ff->nd)
  786. + {
  787. + snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
  788. + snprintf (dest, destlen, tmp, Context->new);
  789. + }
  790. + else if (option (OPTMARKOLD) &&
  791. + folder->ff->nd->lastCached >= folder->ff->nd->firstMessage &&
  792. + folder->ff->nd->lastCached <= folder->ff->nd->lastMessage)
  793. + {
  794. + snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
  795. + snprintf (dest, destlen, tmp, folder->ff->nd->lastMessage - folder->ff->nd->lastCached);
  796. + }
  797. + else
  798. + {
  799. + snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
  800. + snprintf (dest, destlen, tmp, folder->ff->nd->unread);
  801. + }
  802. + break;
  803. +
  804. + case 'd':
  805. + if (folder->ff->nd->desc != NULL)
  806. + {
  807. + char *buf = safe_strdup (folder->ff->nd->desc);
  808. + if (NewsgroupsCharset && *NewsgroupsCharset)
  809. + mutt_convert_string (&buf, NewsgroupsCharset, Charset, M_ICONV_HOOK_FROM);
  810. + mutt_filter_unprintable (&buf);
  811. +
  812. + snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
  813. + snprintf (dest, destlen, tmp, buf);
  814. + }
  815. + else
  816. + {
  817. + snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
  818. + snprintf (dest, destlen, tmp, "");
  819. + }
  820. + break;
  821. + }
  822. + return (src);
  823. +}
  824. +#endif /* USE_NNTP */
  825. +
  826. static void add_folder (MUTTMENU *m, struct browser_state *state,
  827. - const char *name, const struct stat *s, unsigned int new)
  828. + const char *name, const struct stat *s,
  829. + void *data, unsigned int new)
  830. {
  831. if (state->entrylen == state->entrymax)
  832. {
  833. @@ -355,6 +482,10 @@ static void add_folder (MUTTMENU *m, struct browser_state *state,
  834. #ifdef USE_IMAP
  835. (state->entry)[state->entrylen].imap = 0;
  836. #endif
  837. +#ifdef USE_NNTP
  838. + if (option (OPTNEWS))
  839. + (state->entry)[state->entrylen].nd = (NNTP_DATA *)data;
  840. +#endif
  841. (state->entrylen)++;
  842. }
  843. @@ -370,9 +501,36 @@ static void init_state (struct browser_state *state, MUTTMENU *menu)
  844. menu->data = state->entry;
  845. }
  846. +/* get list of all files/newsgroups with mask */
  847. static int examine_directory (MUTTMENU *menu, struct browser_state *state,
  848. char *d, const char *prefix)
  849. {
  850. +#ifdef USE_NNTP
  851. + if (option (OPTNEWS))
  852. + {
  853. + NNTP_SERVER *nserv = CurrentNewsSrv;
  854. + unsigned int i;
  855. +
  856. +/* mutt_buffy_check (0); */
  857. + init_state (state, menu);
  858. +
  859. + for (i = 0; i < nserv->groups_num; i++)
  860. + {
  861. + NNTP_DATA *nntp_data = nserv->groups_list[i];
  862. + if (!nntp_data)
  863. + continue;
  864. + if (prefix && *prefix &&
  865. + strncmp (prefix, nntp_data->group, strlen (prefix)))
  866. + continue;
  867. + if (!((regexec (Mask.rx, nntp_data->group, 0, NULL, 0) == 0) ^ Mask.not))
  868. + continue;
  869. + add_folder (menu, state, nntp_data->group, NULL,
  870. + nntp_data, nntp_data->new);
  871. + }
  872. + }
  873. + else
  874. +#endif /* USE_NNTP */
  875. + {
  876. struct stat s;
  877. DIR *dp;
  878. struct dirent *de;
  879. @@ -433,17 +591,41 @@ static int examine_directory (MUTTMENU *menu, struct browser_state *state,
  880. tmp = Incoming;
  881. while (tmp && mutt_strcmp (buffer, tmp->path))
  882. tmp = tmp->next;
  883. - add_folder (menu, state, de->d_name, &s, (tmp) ? tmp->new : 0);
  884. + add_folder (menu, state, de->d_name, &s, NULL, (tmp) ? tmp->new : 0);
  885. }
  886. closedir (dp);
  887. + }
  888. browser_sort (state);
  889. return 0;
  890. }
  891. +/* get list of mailboxes/subscribed newsgroups */
  892. static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
  893. {
  894. struct stat s;
  895. char buffer[LONG_STRING];
  896. +
  897. +#ifdef USE_NNTP
  898. + if (option (OPTNEWS))
  899. + {
  900. + NNTP_SERVER *nserv = CurrentNewsSrv;
  901. + unsigned int i;
  902. +
  903. +/* mutt_buffy_check (0); */
  904. + init_state (state, menu);
  905. +
  906. + for (i = 0; i < nserv->groups_num; i++)
  907. + {
  908. + NNTP_DATA *nntp_data = nserv->groups_list[i];
  909. + if (nntp_data && (nntp_data->new || (nntp_data->subscribed &&
  910. + (nntp_data->unread || !option (OPTSHOWONLYUNREAD)))))
  911. + add_folder (menu, state, nntp_data->group, NULL,
  912. + nntp_data, nntp_data->new);
  913. + }
  914. + }
  915. + else
  916. +#endif
  917. + {
  918. BUFFY *tmp = Incoming;
  919. #ifdef USE_IMAP
  920. struct mailbox_state mbox;
  921. @@ -461,14 +643,21 @@ static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
  922. if (mx_is_imap (tmp->path))
  923. {
  924. imap_mailbox_state (tmp->path, &mbox);
  925. - add_folder (menu, state, tmp->path, NULL, mbox.new);
  926. + add_folder (menu, state, tmp->path, NULL, NULL, mbox.new);
  927. continue;
  928. }
  929. #endif
  930. #ifdef USE_POP
  931. if (mx_is_pop (tmp->path))
  932. {
  933. - add_folder (menu, state, tmp->path, NULL, tmp->new);
  934. + add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
  935. + continue;
  936. + }
  937. +#endif
  938. +#ifdef USE_NNTP
  939. + if (mx_is_nntp (tmp->path))
  940. + {
  941. + add_folder (menu, state, tmp->path, NULL, NULL, tmp->new);
  942. continue;
  943. }
  944. #endif
  945. @@ -497,15 +686,20 @@ static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
  946. strfcpy (buffer, NONULL(tmp->path), sizeof (buffer));
  947. mutt_pretty_mailbox (buffer, sizeof (buffer));
  948. - add_folder (menu, state, buffer, &s, tmp->new);
  949. + add_folder (menu, state, buffer, &s, NULL, tmp->new);
  950. }
  951. while ((tmp = tmp->next));
  952. + }
  953. browser_sort (state);
  954. return 0;
  955. }
  956. static int select_file_search (MUTTMENU *menu, regex_t *re, int n)
  957. {
  958. +#ifdef USE_NNTP
  959. + if (option (OPTNEWS))
  960. + return (regexec (re, ((struct folder_file *) menu->data)[n].desc, 0, NULL, 0));
  961. +#endif
  962. return (regexec (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
  963. }
  964. @@ -516,6 +710,12 @@ static void folder_entry (char *s, size_t slen, MUTTMENU *menu, int num)
  965. folder.ff = &((struct folder_file *) menu->data)[num];
  966. folder.num = num;
  967. +#ifdef USE_NNTP
  968. + if (option (OPTNEWS))
  969. + mutt_FormatString (s, slen, 0, NONULL(GroupFormat), newsgroup_format_str,
  970. + (unsigned long) &folder, M_FORMAT_ARROWCURSOR);
  971. + else
  972. +#endif
  973. mutt_FormatString (s, slen, 0, NONULL(FolderFormat), folder_format_str,
  974. (unsigned long) &folder, M_FORMAT_ARROWCURSOR);
  975. }
  976. @@ -536,6 +736,17 @@ static void init_menu (struct browser_state *state, MUTTMENU *menu, char *title,
  977. menu->tagged = 0;
  978. +#ifdef USE_NNTP
  979. + if (option (OPTNEWS))
  980. + {
  981. + if (buffy)
  982. + snprintf (title, titlelen, _("Subscribed newsgroups"));
  983. + else
  984. + snprintf (title, titlelen, _("Newsgroups on server [%s]"),
  985. + CurrentNewsSrv->conn->account.host);
  986. + }
  987. + else
  988. +#endif
  989. if (buffy)
  990. {
  991. menu->is_mailbox_list = 1;
  992. @@ -609,6 +820,31 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *num
  993. if (!folder)
  994. strfcpy (LastDirBackup, LastDir, sizeof (LastDirBackup));
  995. +#ifdef USE_NNTP
  996. + if (option (OPTNEWS))
  997. + {
  998. + if (*f)
  999. + strfcpy (prefix, f, sizeof (prefix));
  1000. + else
  1001. + {
  1002. + NNTP_SERVER *nserv = CurrentNewsSrv;
  1003. + unsigned int i;
  1004. +
  1005. + /* default state for news reader mode is browse subscribed newsgroups */
  1006. + buffy = 0;
  1007. + for (i = 0; i < nserv->groups_num; i++)
  1008. + {
  1009. + NNTP_DATA *nntp_data = nserv->groups_list[i];
  1010. + if (nntp_data && nntp_data->subscribed)
  1011. + {
  1012. + buffy = 1;
  1013. + break;
  1014. + }
  1015. + }
  1016. + }
  1017. + }
  1018. + else
  1019. +#endif
  1020. if (*f)
  1021. {
  1022. mutt_expand_path (f, flen);
  1023. @@ -705,6 +941,9 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *num
  1024. menu->tag = file_tag;
  1025. menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER,
  1026. +#ifdef USE_NNTP
  1027. + option (OPTNEWS) ? FolderNewsHelp :
  1028. +#endif
  1029. FolderHelp);
  1030. init_menu (&state, menu, title, sizeof (title), buffy);
  1031. @@ -842,7 +1081,11 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *num
  1032. }
  1033. }
  1034. +#ifdef USE_NNTP
  1035. + if (buffy || option (OPTNEWS))
  1036. +#else
  1037. if (buffy)
  1038. +#endif
  1039. {
  1040. strfcpy (f, state.entry[menu->current].name, flen);
  1041. mutt_expand_path (f, flen);
  1042. @@ -900,14 +1143,6 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *num
  1043. break;
  1044. #ifdef USE_IMAP
  1045. - case OP_BROWSER_SUBSCRIBE:
  1046. - imap_subscribe (state.entry[menu->current].name, 1);
  1047. - break;
  1048. -
  1049. - case OP_BROWSER_UNSUBSCRIBE:
  1050. - imap_subscribe (state.entry[menu->current].name, 0);
  1051. - break;
  1052. -
  1053. case OP_BROWSER_TOGGLE_LSUB:
  1054. if (option (OPTIMAPLSUB))
  1055. unset_option (OPTIMAPLSUB);
  1056. @@ -1008,6 +1243,11 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *num
  1057. case OP_CHANGE_DIRECTORY:
  1058. +#ifdef USE_NNTP
  1059. + if (option (OPTNEWS))
  1060. + break;
  1061. +#endif
  1062. +
  1063. strfcpy (buf, LastDir, sizeof (buf));
  1064. #ifdef USE_IMAP
  1065. if (!state.imap_browse)
  1066. @@ -1273,6 +1513,210 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *num
  1067. else
  1068. mutt_error _("Error trying to view file");
  1069. }
  1070. + break;
  1071. +
  1072. +#ifdef USE_NNTP
  1073. + case OP_CATCHUP:
  1074. + case OP_UNCATCHUP:
  1075. + if (option (OPTNEWS))
  1076. + {
  1077. + struct folder_file *f = &state.entry[menu->current];
  1078. + int rc;
  1079. + NNTP_DATA *nntp_data;
  1080. +
  1081. + rc = nntp_newsrc_parse (CurrentNewsSrv);
  1082. + if (rc < 0)
  1083. + break;
  1084. +
  1085. + if (i == OP_CATCHUP)
  1086. + nntp_data = mutt_newsgroup_catchup (CurrentNewsSrv, f->name);
  1087. + else
  1088. + nntp_data = mutt_newsgroup_uncatchup (CurrentNewsSrv, f->name);
  1089. +
  1090. + if (nntp_data)
  1091. + {
  1092. +/* FOLDER folder;
  1093. + struct folder_file ff;
  1094. + char buffer[_POSIX_PATH_MAX + SHORT_STRING];
  1095. +
  1096. + folder.ff = &ff;
  1097. + folder.ff->name = f->name;
  1098. + folder.ff->st = NULL;
  1099. + folder.ff->is_new = nntp_data->new;
  1100. + folder.ff->nntp_data = nntp_data;
  1101. + FREE (&f->desc);
  1102. + mutt_FormatString (buffer, sizeof (buffer), 0, NONULL(GroupFormat),
  1103. + newsgroup_format_str, (unsigned long) &folder,
  1104. + M_FORMAT_ARROWCURSOR);
  1105. + f->desc = safe_strdup (buffer); */
  1106. + nntp_newsrc_update (CurrentNewsSrv);
  1107. + if (menu->current + 1 < menu->max)
  1108. + menu->current++;
  1109. + menu->redraw = REDRAW_MOTION_RESYNCH;
  1110. + }
  1111. + if (rc)
  1112. + menu->redraw = REDRAW_INDEX;
  1113. + nntp_newsrc_close (CurrentNewsSrv);
  1114. + }
  1115. + break;
  1116. +
  1117. + case OP_LOAD_ACTIVE:
  1118. + if (option (OPTNEWS))
  1119. + {
  1120. + NNTP_SERVER *nserv = CurrentNewsSrv;
  1121. + unsigned int i;
  1122. +
  1123. + if (nntp_newsrc_parse (nserv) < 0)
  1124. + break;
  1125. +
  1126. + for (i = 0; i < nserv->groups_num; i++)
  1127. + {
  1128. + NNTP_DATA *nntp_data = nserv->groups_list[i];
  1129. + if (nntp_data)
  1130. + nntp_data->deleted = 1;
  1131. + }
  1132. + nntp_active_fetch (nserv);
  1133. + nntp_newsrc_update (nserv);
  1134. + nntp_newsrc_close (nserv);
  1135. +
  1136. + destroy_state (&state);
  1137. + if (buffy)
  1138. + examine_mailboxes (menu, &state);
  1139. + else
  1140. + examine_directory (menu, &state, NULL, NULL);
  1141. + init_menu (&state, menu, title, sizeof (title), buffy);
  1142. + }
  1143. + break;
  1144. +#endif /* USE_NNTP */
  1145. +
  1146. +#if defined USE_IMAP || defined USE_NNTP
  1147. + case OP_BROWSER_SUBSCRIBE:
  1148. + case OP_BROWSER_UNSUBSCRIBE:
  1149. +#endif
  1150. +#ifdef USE_NNTP
  1151. + case OP_SUBSCRIBE_PATTERN:
  1152. + case OP_UNSUBSCRIBE_PATTERN:
  1153. + if (option (OPTNEWS))
  1154. + {
  1155. + NNTP_SERVER *nserv = CurrentNewsSrv;
  1156. + NNTP_DATA *nntp_data;
  1157. + regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
  1158. + char *s = buf;
  1159. + int rc, j = menu->current;
  1160. +
  1161. + if (i == OP_SUBSCRIBE_PATTERN || i == OP_UNSUBSCRIBE_PATTERN)
  1162. + {
  1163. + char tmp[STRING];
  1164. + int err;
  1165. +
  1166. + buf[0] = 0;
  1167. + if (i == OP_SUBSCRIBE_PATTERN)
  1168. + snprintf (tmp, sizeof (tmp), _("Subscribe pattern: "));
  1169. + else
  1170. + snprintf (tmp, sizeof (tmp), _("Unsubscribe pattern: "));
  1171. + if (mutt_get_field (tmp, buf, sizeof (buf), 0) != 0 || !buf[0])
  1172. + {
  1173. + FREE (&rx);
  1174. + break;
  1175. + }
  1176. +
  1177. + err = REGCOMP (rx, s, REG_NOSUB);
  1178. + if (err)
  1179. + {
  1180. + regerror (err, rx, buf, sizeof (buf));
  1181. + regfree (rx);
  1182. + FREE (&rx);
  1183. + mutt_error ("%s", buf);
  1184. + break;
  1185. + }
  1186. + menu->redraw = REDRAW_FULL;
  1187. + j = 0;
  1188. + }
  1189. + else if (!state.entrylen)
  1190. + {
  1191. + mutt_error _("No newsgroups match the mask");
  1192. + break;
  1193. + }
  1194. +
  1195. + rc = nntp_newsrc_parse (nserv);
  1196. + if (rc < 0)
  1197. + break;
  1198. +
  1199. + for ( ; j < state.entrylen; j++)
  1200. + {
  1201. + struct folder_file *f = &state.entry[j];
  1202. +
  1203. + if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE ||
  1204. + regexec (rx, f->name, 0, NULL, 0) == 0)
  1205. + {
  1206. + if (i == OP_BROWSER_SUBSCRIBE || i == OP_SUBSCRIBE_PATTERN)
  1207. + nntp_data = mutt_newsgroup_subscribe (nserv, f->name);
  1208. + else
  1209. + nntp_data = mutt_newsgroup_unsubscribe (nserv, f->name);
  1210. +/* if (nntp_data)
  1211. + {
  1212. + FOLDER folder;
  1213. + char buffer[_POSIX_PATH_MAX + SHORT_STRING];
  1214. +
  1215. + folder.name = f->name;
  1216. + folder.f = NULL;
  1217. + folder.new = nntp_data->new;
  1218. + folder.nd = nntp_data;
  1219. + FREE (&f->desc);
  1220. + mutt_FormatString (buffer, sizeof (buffer), 0, NONULL(GroupFormat),
  1221. + newsgroup_format_str, (unsigned long) &folder,
  1222. + M_FORMAT_ARROWCURSOR);
  1223. + f->desc = safe_strdup (buffer);
  1224. + } */
  1225. + }
  1226. + if (i == OP_BROWSER_SUBSCRIBE || i == OP_BROWSER_UNSUBSCRIBE)
  1227. + {
  1228. + if (menu->current + 1 < menu->max)
  1229. + menu->current++;
  1230. + menu->redraw = REDRAW_MOTION_RESYNCH;
  1231. + break;
  1232. + }
  1233. + }
  1234. + if (i == OP_SUBSCRIBE_PATTERN)
  1235. + {
  1236. + unsigned int i;
  1237. +
  1238. + for (i = 0; nserv && i < nserv->groups_num; i++)
  1239. + {
  1240. + nntp_data = nserv->groups_list[i];
  1241. + if (nntp_data && nntp_data->group && !nntp_data->subscribed)
  1242. + {
  1243. + if (regexec (rx, nntp_data->group, 0, NULL, 0) == 0)
  1244. + {
  1245. + mutt_newsgroup_subscribe (nserv, nntp_data->group);
  1246. + add_folder (menu, &state, nntp_data->group, NULL,
  1247. + nntp_data, nntp_data->new);
  1248. + }
  1249. + }
  1250. + }
  1251. + init_menu (&state, menu, title, sizeof (title), buffy);
  1252. + }
  1253. + if (rc > 0)
  1254. + menu->redraw = REDRAW_FULL;
  1255. + nntp_newsrc_update (nserv);
  1256. + nntp_clear_cache (nserv);
  1257. + nntp_newsrc_close (nserv);
  1258. + if (i != OP_BROWSER_SUBSCRIBE && i != OP_BROWSER_UNSUBSCRIBE)
  1259. + regfree (rx);
  1260. + FREE (&rx);
  1261. + }
  1262. +#ifdef USE_IMAP
  1263. + else
  1264. +#endif /* USE_IMAP && USE_NNTP */
  1265. +#endif /* USE_NNTP */
  1266. +#ifdef USE_IMAP
  1267. + {
  1268. + if (i == OP_BROWSER_SUBSCRIBE)
  1269. + imap_subscribe (state.entry[menu->current].name, 1);
  1270. + else
  1271. + imap_subscribe (state.entry[menu->current].name, 0);
  1272. + }
  1273. +#endif /* USE_IMAP */
  1274. }
  1275. }
  1276. diff --git a/browser.h b/browser.h
  1277. index 515d69f..ad89ab2 100644
  1278. --- a/browser.h
  1279. +++ b/browser.h
  1280. @@ -19,6 +19,10 @@
  1281. #ifndef _BROWSER_H
  1282. #define _BROWSER_H 1
  1283. +#ifdef USE_NNTP
  1284. +#include "nntp.h"
  1285. +#endif
  1286. +
  1287. struct folder_file
  1288. {
  1289. mode_t mode;
  1290. @@ -37,6 +41,9 @@ struct folder_file
  1291. unsigned selectable : 1;
  1292. unsigned inferiors : 1;
  1293. #endif
  1294. +#ifdef USE_NNTP
  1295. + NNTP_DATA *nd;
  1296. +#endif
  1297. unsigned tagged : 1;
  1298. };
  1299. diff --git a/buffy.c b/buffy.c
  1300. index 225104d..b1abfa9 100644
  1301. --- a/buffy.c
  1302. +++ b/buffy.c
  1303. @@ -525,6 +525,9 @@ int mutt_buffy_check (int force)
  1304. /* check device ID and serial number instead of comparing paths */
  1305. if (!Context || Context->magic == M_IMAP || Context->magic == M_POP
  1306. +#ifdef USE_NNTP
  1307. + || Context->magic == M_NNTP
  1308. +#endif
  1309. || stat (Context->path, &contex_sb) != 0)
  1310. {
  1311. contex_sb.st_dev=0;
  1312. @@ -541,6 +544,11 @@ int mutt_buffy_check (int force)
  1313. tmp->magic = M_POP;
  1314. else
  1315. #endif
  1316. +#ifdef USE_NNTP
  1317. + if ((tmp->magic == M_NNTP) || mx_is_nntp (tmp->path))
  1318. + tmp->magic = M_NNTP;
  1319. + else
  1320. +#endif
  1321. if (stat (tmp->path, &sb) != 0 || (S_ISREG(sb.st_mode) && sb.st_size == 0) ||
  1322. (!tmp->magic && (tmp->magic = mx_get_magic (tmp->path)) <= 0))
  1323. {
  1324. @@ -556,7 +564,11 @@ int mutt_buffy_check (int force)
  1325. /* check to see if the folder is the currently selected folder
  1326. * before polling */
  1327. if (!Context || !Context->path ||
  1328. +#ifdef USE_NNTP
  1329. + (( tmp->magic == M_IMAP || tmp->magic == M_POP || tmp->magic == M_NNTP )
  1330. +#else
  1331. (( tmp->magic == M_IMAP || tmp->magic == M_POP )
  1332. +#endif
  1333. ? mutt_strcmp (tmp->path, Context->path) :
  1334. (sb.st_dev != contex_sb.st_dev || sb.st_ino != contex_sb.st_ino)))
  1335. {
  1336. diff --git a/complete.c b/complete.c
  1337. index d0ee4af..8dc48cd 100644
  1338. --- a/complete.c
  1339. +++ b/complete.c
  1340. @@ -25,6 +25,9 @@
  1341. #include "mailbox.h"
  1342. #include "imap.h"
  1343. #endif
  1344. +#ifdef USE_NNTP
  1345. +#include "nntp.h"
  1346. +#endif
  1347. #include <dirent.h>
  1348. #include <string.h>
  1349. @@ -48,9 +51,70 @@ int mutt_complete (char *s, size_t slen)
  1350. char filepart[_POSIX_PATH_MAX];
  1351. #ifdef USE_IMAP
  1352. char imap_path[LONG_STRING];
  1353. +#endif
  1354. dprint (2, (debugfile, "mutt_complete: completing %s\n", s));
  1355. +#ifdef USE_NNTP
  1356. + if (option (OPTNEWS))
  1357. + {
  1358. + NNTP_SERVER *nserv = CurrentNewsSrv;
  1359. + unsigned int n = 0;
  1360. +
  1361. + strfcpy (filepart, s, sizeof (filepart));
  1362. +
  1363. + /* special case to handle when there is no filepart yet
  1364. + * find the first subscribed newsgroup */
  1365. + len = mutt_strlen (filepart);
  1366. + if (len == 0)
  1367. + {
  1368. + for (; n < nserv->groups_num; n++)
  1369. + {
  1370. + NNTP_DATA *nntp_data = nserv->groups_list[n];
  1371. +
  1372. + if (nntp_data && nntp_data->subscribed)
  1373. + {
  1374. + strfcpy (filepart, nntp_data->group, sizeof (filepart));
  1375. + init = 1;
  1376. + n++;
  1377. + break;
  1378. + }
  1379. + }
  1380. + }
  1381. +
  1382. + for (; n < nserv->groups_num; n++)
  1383. + {
  1384. + NNTP_DATA *nntp_data = nserv->groups_list[n];
  1385. +
  1386. + if (nntp_data && nntp_data->subscribed &&
  1387. + mutt_strncmp (nntp_data->group, filepart, len) == 0)
  1388. + {
  1389. + if (init)
  1390. + {
  1391. + for (i = 0; filepart[i] && nntp_data->group[i]; i++)
  1392. + {
  1393. + if (filepart[i] != nntp_data->group[i])
  1394. + {
  1395. + filepart[i] = 0;
  1396. + break;
  1397. + }
  1398. + }
  1399. + filepart[i] = 0;
  1400. + }
  1401. + else
  1402. + {
  1403. + strfcpy (filepart, nntp_data->group, sizeof (filepart));
  1404. + init = 1;
  1405. + }
  1406. + }
  1407. + }
  1408. +
  1409. + strcpy (s, filepart);
  1410. + return (init ? 0 : -1);
  1411. + }
  1412. +#endif
  1413. +
  1414. +#ifdef USE_IMAP
  1415. /* we can use '/' as a delimiter, imap_complete rewrites it */
  1416. if (*s == '=' || *s == '+' || *s == '!')
  1417. {
  1418. diff --git a/compose.c b/compose.c
  1419. index 16576f2..9e23dfd 100644
  1420. --- a/compose.c
  1421. +++ b/compose.c
  1422. @@ -33,11 +33,16 @@
  1423. #include "sort.h"
  1424. #include "charset.h"
  1425. #include "sidebar.h"
  1426. +#include "mx.h"
  1427. #ifdef MIXMASTER
  1428. #include "remailer.h"
  1429. #endif
  1430. +#ifdef USE_NNTP
  1431. +#include "nntp.h"
  1432. +#endif
  1433. +
  1434. #include <errno.h>
  1435. #include <string.h>
  1436. #include <sys/stat.h>
  1437. @@ -68,11 +73,17 @@ enum
  1438. HDR_CRYPT,
  1439. HDR_CRYPTINFO,
  1440. +#ifdef USE_NNTP
  1441. + HDR_NEWSGROUPS,
  1442. + HDR_FOLLOWUPTO,
  1443. + HDR_XCOMMENTTO,
  1444. +#endif
  1445. +
  1446. HDR_ATTACH = (HDR_FCC + 5) /* where to start printing the attachments */
  1447. };
  1448. -#define HDR_XOFFSET 10
  1449. -#define TITLE_FMT "%10s" /* Used for Prompts, which are ASCII */
  1450. +#define HDR_XOFFSET 14
  1451. +#define TITLE_FMT "%14s" /* Used for Prompts, which are ASCII */
  1452. #define W (COLS - HDR_XOFFSET - SidebarWidth)
  1453. static const char * const Prompts[] =
  1454. @@ -84,6 +95,16 @@ static const char * const Prompts[] =
  1455. "Subject: ",
  1456. "Reply-To: ",
  1457. "Fcc: "
  1458. +#ifdef USE_NNTP
  1459. +#ifdef MIXMASTER
  1460. + ,""
  1461. +#endif
  1462. + ,""
  1463. + ,""
  1464. + ,"Newsgroups: "
  1465. + ,"Followup-To: "
  1466. + ,"X-Comment-To: "
  1467. +#endif
  1468. };
  1469. static const struct mapping_t ComposeHelp[] = {
  1470. @@ -98,6 +119,19 @@ static const struct mapping_t ComposeHelp[] = {
  1471. { NULL, 0 }
  1472. };
  1473. +#ifdef USE_NNTP
  1474. +static struct mapping_t ComposeNewsHelp[] = {
  1475. + { N_("Send"), OP_COMPOSE_SEND_MESSAGE },
  1476. + { N_("Abort"), OP_EXIT },
  1477. + { "Newsgroups", OP_COMPOSE_EDIT_NEWSGROUPS },
  1478. + { "Subj", OP_COMPOSE_EDIT_SUBJECT },
  1479. + { N_("Attach file"), OP_COMPOSE_ATTACH_FILE },
  1480. + { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION },
  1481. + { N_("Help"), OP_HELP },
  1482. + { NULL, 0 }
  1483. +};
  1484. +#endif
  1485. +
  1486. static void snd_entry (char *b, size_t blen, MUTTMENU *menu, int num)
  1487. {
  1488. mutt_FormatString (b, blen, 0, NONULL (AttachFormat), mutt_attach_fmt,
  1489. @@ -111,7 +145,7 @@ static void snd_entry (char *b, size_t blen, MUTTMENU *menu, int num)
  1490. static void redraw_crypt_lines (HEADER *msg)
  1491. {
  1492. - mvaddstr (HDR_CRYPT, SidebarWidth, "Security: ");
  1493. + mvprintw (HDR_CRYPT, SidebarWidth, TITLE_FMT, "Security: ");
  1494. if ((WithCrypto & (APPLICATION_PGP | APPLICATION_SMIME)) == 0)
  1495. {
  1496. @@ -149,10 +183,11 @@ static void redraw_crypt_lines (HEADER *msg)
  1497. if ((WithCrypto & APPLICATION_PGP)
  1498. && (msg->security & APPLICATION_PGP) && (msg->security & SIGN))
  1499. printw ("%s%s", _(" sign as: "), PgpSignAs ? PgpSignAs : _("<default>"));
  1500. + printw (TITLE_FMT "%s", _(" sign as: "), PgpSignAs ? PgpSignAs : _("<default>"));
  1501. if ((WithCrypto & APPLICATION_SMIME)
  1502. && (msg->security & APPLICATION_SMIME) && (msg->security & SIGN)) {
  1503. - printw ("%s%s", _(" sign as: "), SmimeDefaultKey ? SmimeDefaultKey : _("<default>"));
  1504. + printw (TITLE_FMT "%s", _(" sign as: "), SmimeDefaultKey ? SmimeDefaultKey : _("<default>"));
  1505. }
  1506. if ((WithCrypto & APPLICATION_SMIME)
  1507. @@ -173,7 +208,7 @@ static void redraw_mix_line (LIST *chain)
  1508. int c;
  1509. char *t;
  1510. - mvaddstr (HDR_MIX, SidebarWidth, " Mix: ");
  1511. + mvprintw (HDR_MIX, SidebarWidth, TITLE_FMT, "Mix: ");
  1512. if (!chain)
  1513. {
  1514. @@ -248,9 +283,28 @@ static void draw_envelope (HEADER *msg, char *fcc)
  1515. {
  1516. draw_sidebar (MENU_COMPOSE);
  1517. draw_envelope_addr (HDR_FROM, msg->env->from);
  1518. +#ifdef USE_NNTP
  1519. + if (!option (OPTNEWSSEND))
  1520. + {
  1521. +#endif
  1522. draw_envelope_addr (HDR_TO, msg->env->to);
  1523. draw_envelope_addr (HDR_CC, msg->env->cc);
  1524. draw_envelope_addr (HDR_BCC, msg->env->bcc);
  1525. +#ifdef USE_NNTP
  1526. + }
  1527. + else
  1528. + {
  1529. + mvprintw (HDR_TO, 0, TITLE_FMT , Prompts[HDR_NEWSGROUPS - 1]);
  1530. + mutt_paddstr (W, NONULL (msg->env->newsgroups));
  1531. + mvprintw (HDR_CC, 0, TITLE_FMT , Prompts[HDR_FOLLOWUPTO - 1]);
  1532. + mutt_paddstr (W, NONULL (msg->env->followup_to));
  1533. + if (option (OPTXCOMMENTTO))
  1534. + {
  1535. + mvprintw (HDR_BCC, 0, TITLE_FMT , Prompts[HDR_XCOMMENTTO - 1]);
  1536. + mutt_paddstr (W, NONULL (msg->env->x_comment_to));
  1537. + }
  1538. + }
  1539. +#endif
  1540. mvprintw (HDR_SUBJECT, SidebarWidth, TITLE_FMT, Prompts[HDR_SUBJECT - 1]);
  1541. mutt_paddstr (W, NONULL (msg->env->subject));
  1542. draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to);
  1543. @@ -501,6 +555,12 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
  1544. /* Sort, SortAux could be changed in mutt_index_menu() */
  1545. int oldSort, oldSortAux;
  1546. struct stat st;
  1547. +#ifdef USE_NNTP
  1548. + int news = 0; /* is it a news article ? */
  1549. +
  1550. + if (option (OPTNEWSSEND))
  1551. + news++;
  1552. +#endif
  1553. mutt_attach_init (msg->content);
  1554. idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1);
  1555. @@ -511,10 +571,18 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
  1556. menu->make_entry = snd_entry;
  1557. menu->tag = mutt_tag_attach;
  1558. menu->data = idx;
  1559. +#ifdef USE_NNTP
  1560. + if (news)
  1561. + menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeNewsHelp);
  1562. + else
  1563. +#endif
  1564. menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp);
  1565. while (loop)
  1566. {
  1567. +#ifdef USE_NNTP
  1568. + unset_option (OPTNEWS); /* for any case */
  1569. +#endif
  1570. switch (op = mutt_menuLoop (menu))
  1571. {
  1572. case OP_REDRAW:
  1573. @@ -527,17 +595,90 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
  1574. mutt_message_hook (NULL, msg, M_SEND2HOOK);
  1575. break;
  1576. case OP_COMPOSE_EDIT_TO:
  1577. +#ifdef USE_NNTP
  1578. + if (news)
  1579. + break;
  1580. +#endif
  1581. menu->redraw = edit_address_list (HDR_TO, &msg->env->to);
  1582. mutt_message_hook (NULL, msg, M_SEND2HOOK);
  1583. break;
  1584. case OP_COMPOSE_EDIT_BCC:
  1585. +#ifdef USE_NNTP
  1586. + if (news)
  1587. + break;
  1588. +#endif
  1589. menu->redraw = edit_address_list (HDR_BCC, &msg->env->bcc);
  1590. mutt_message_hook (NULL, msg, M_SEND2HOOK);
  1591. break;
  1592. case OP_COMPOSE_EDIT_CC:
  1593. +#ifdef USE_NNTP
  1594. + if (news)
  1595. + break;
  1596. +#endif
  1597. menu->redraw = edit_address_list (HDR_CC, &msg->env->cc);
  1598. mutt_message_hook (NULL, msg, M_SEND2HOOK);
  1599. break;
  1600. +#ifdef USE_NNTP
  1601. + case OP_COMPOSE_EDIT_NEWSGROUPS:
  1602. + if (news)
  1603. + {
  1604. + if (msg->env->newsgroups)
  1605. + strfcpy (buf, msg->env->newsgroups, sizeof (buf));
  1606. + else
  1607. + buf[0] = 0;
  1608. + if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) == 0 &&
  1609. + buf[0])
  1610. + {
  1611. + FREE (&msg->env->newsgroups);
  1612. + mutt_remove_trailing_ws (buf);
  1613. + msg->env->newsgroups = safe_strdup (mutt_skip_whitespace (buf));
  1614. + move (HDR_TO, HDR_XOFFSET);
  1615. + clrtoeol ();
  1616. + if (msg->env->newsgroups)
  1617. + printw ("%-*.*s", W, W, msg->env->newsgroups);
  1618. + }
  1619. + }
  1620. + break;
  1621. +
  1622. + case OP_COMPOSE_EDIT_FOLLOWUP_TO:
  1623. + if (news)
  1624. + {
  1625. + buf[0] = 0;
  1626. + if (msg->env->followup_to)
  1627. + strfcpy (buf, msg->env->followup_to, sizeof (buf));
  1628. + if (mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) == 0 &&
  1629. + buf[0])
  1630. + {
  1631. + FREE (&msg->env->followup_to);
  1632. + mutt_remove_trailing_ws (buf);
  1633. + msg->env->followup_to = safe_strdup (mutt_skip_whitespace (buf));
  1634. + move (HDR_CC, HDR_XOFFSET);
  1635. + clrtoeol ();
  1636. + if (msg->env->followup_to)
  1637. + printw ("%-*.*s", W, W, msg->env->followup_to);
  1638. + }
  1639. + }
  1640. + break;
  1641. +
  1642. + case OP_COMPOSE_EDIT_X_COMMENT_TO:
  1643. + if (news && option (OPTXCOMMENTTO))
  1644. + {
  1645. + buf[0] = 0;
  1646. + if (msg->env->x_comment_to)
  1647. + strfcpy (buf, msg->env->x_comment_to, sizeof (buf));
  1648. + if (mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) == 0 &&
  1649. + buf[0])
  1650. + {
  1651. + FREE (&msg->env->x_comment_to);
  1652. + msg->env->x_comment_to = safe_strdup (buf);
  1653. + move (HDR_BCC, HDR_XOFFSET);
  1654. + clrtoeol ();
  1655. + if (msg->env->x_comment_to)
  1656. + printw ("%-*.*s", W, W, msg->env->x_comment_to);
  1657. + }
  1658. + }
  1659. + break;
  1660. +#endif
  1661. case OP_COMPOSE_EDIT_SUBJECT:
  1662. if (msg->env->subject)
  1663. strfcpy (buf, msg->env->subject, sizeof (buf));
  1664. @@ -701,6 +842,9 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
  1665. break;
  1666. case OP_COMPOSE_ATTACH_MESSAGE:
  1667. +#ifdef USE_NNTP
  1668. + case OP_COMPOSE_ATTACH_NEWS_MESSAGE:
  1669. +#endif
  1670. {
  1671. char *prompt;
  1672. HEADER *h;
  1673. @@ -708,7 +852,22 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
  1674. fname[0] = 0;
  1675. prompt = _("Open mailbox to attach message from");
  1676. +#ifdef USE_NNTP
  1677. + unset_option (OPTNEWS);
  1678. + if (op == OP_COMPOSE_ATTACH_NEWS_MESSAGE)
  1679. + {
  1680. + if (!(CurrentNewsSrv = nntp_select_server (NewsServer, 0)))
  1681. + break;
  1682. +
  1683. + prompt = _("Open newsgroup to attach message from");
  1684. + set_option (OPTNEWS);
  1685. + }
  1686. +#endif
  1687. +
  1688. if (Context)
  1689. +#ifdef USE_NNTP
  1690. + if ((op == OP_COMPOSE_ATTACH_MESSAGE) ^ (Context->magic == M_NNTP))
  1691. +#endif
  1692. {
  1693. strfcpy (fname, NONULL (Context->path), sizeof (fname));
  1694. mutt_pretty_mailbox (fname, sizeof (fname));
  1695. @@ -717,6 +876,11 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
  1696. if (mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 1) == -1 || !fname[0])
  1697. break;
  1698. +#ifdef USE_NNTP
  1699. + if (option (OPTNEWS))
  1700. + nntp_expand_path (fname, sizeof (fname), &CurrentNewsSrv->conn->account);
  1701. + else
  1702. +#endif
  1703. mutt_expand_path (fname, sizeof (fname));
  1704. #ifdef USE_IMAP
  1705. if (!mx_is_imap (fname))
  1706. @@ -724,6 +888,9 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
  1707. #ifdef USE_POP
  1708. if (!mx_is_pop (fname))
  1709. #endif
  1710. +#ifdef USE_NNTP
  1711. + if (!mx_is_nntp (fname) && !option (OPTNEWS))
  1712. +#endif
  1713. /* check to make sure the file exists and is readable */
  1714. if (access (fname, R_OK) == -1)
  1715. {
  1716. diff --git a/configure.ac b/configure.ac
  1717. index 2d57b76..86bd542 100644
  1718. --- a/configure.ac
  1719. +++ b/configure.ac
  1720. @@ -592,6 +592,15 @@ AC_ARG_ENABLE(imap, AS_HELP_STRING([--enable-imap],[Enable IMAP support]),
  1721. ])
  1722. AM_CONDITIONAL(BUILD_IMAP, test x$need_imap = xyes)
  1723. +AC_ARG_ENABLE(nntp, AC_HELP_STRING([--enable-nntp],[Enable NNTP support]),
  1724. +[ if test x$enableval = xyes ; then
  1725. + AC_DEFINE(USE_NNTP,1,[ Define if you want support for the NNTP protocol. ])
  1726. + MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS nntp.o newsrc.o"
  1727. + need_nntp="yes"
  1728. + need_socket="yes"
  1729. + fi
  1730. +])
  1731. +
  1732. AC_ARG_ENABLE(smtp, AS_HELP_STRING([--enable-smtp],[include internal SMTP relay support]),
  1733. [if test $enableval = yes; then
  1734. AC_DEFINE(USE_SMTP, 1, [Include internal SMTP relay support])
  1735. @@ -599,7 +608,7 @@ AC_ARG_ENABLE(smtp, AS_HELP_STRING([--enable-smtp],[include internal SMTP relay
  1736. need_socket="yes"
  1737. fi])
  1738. -if test x"$need_imap" = xyes -o x"$need_pop" = xyes ; then
  1739. +if test x"$need_imap" = xyes -o x"$need_pop" = xyes -o x"$need_nntp" = xyes ; then
  1740. MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS bcache.o"
  1741. fi
  1742. diff --git a/curs_main.c b/curs_main.c
  1743. index 5c58f1c..8cec507 100644
  1744. --- a/curs_main.c
  1745. +++ b/curs_main.c
  1746. @@ -22,6 +22,7 @@
  1747. #include "mutt.h"
  1748. #include "mutt_curses.h"
  1749. +#include "mx.h"
  1750. #include "mutt_menu.h"
  1751. #include "mailbox.h"
  1752. #include "mapping.h"
  1753. @@ -40,6 +41,10 @@
  1754. #include "mutt_crypt.h"
  1755. +#ifdef USE_NNTP
  1756. +#include "nntp.h"
  1757. +#endif
  1758. +
  1759. #include <ctype.h>
  1760. #include <stdlib.h>
  1761. @@ -428,12 +433,27 @@ static const struct mapping_t IndexHelp[] = {
  1762. { NULL, 0 }
  1763. };
  1764. +#ifdef USE_NNTP
  1765. +struct mapping_t IndexNewsHelp[] = {
  1766. + { N_("Quit"), OP_QUIT },
  1767. + { N_("Del"), OP_DELETE },
  1768. + { N_("Undel"), OP_UNDELETE },
  1769. + { N_("Save"), OP_SAVE },
  1770. + { N_("Post"), OP_POST },
  1771. + { N_("Followup"), OP_FOLLOWUP },
  1772. + { N_("Catchup"), OP_CATCHUP },
  1773. + { N_("Help"), OP_HELP },
  1774. + { NULL, 0 }
  1775. +};
  1776. +#endif
  1777. +
  1778. /* This function handles the message index window as well as commands returned
  1779. * from the pager (MENU_PAGER).
  1780. */
  1781. int mutt_index_menu (void)
  1782. {
  1783. char buf[LONG_STRING], helpstr[LONG_STRING];
  1784. + int flags;
  1785. int op = OP_NULL;
  1786. int done = 0; /* controls when to exit the "event" loop */
  1787. int i = 0, j;
  1788. @@ -454,7 +474,11 @@ int mutt_index_menu (void)
  1789. menu->make_entry = index_make_entry;
  1790. menu->color = index_color;
  1791. menu->current = ci_first_message ();
  1792. - menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN, IndexHelp);
  1793. + menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
  1794. +#ifdef USE_NNTP
  1795. + (Context && (Context->magic == M_NNTP)) ? IndexNewsHelp :
  1796. +#endif
  1797. + IndexHelp);
  1798. if (!attach_msg)
  1799. mutt_buffy_check(1); /* force the buffy check after we enter the folder */
  1800. @@ -710,6 +734,9 @@ int mutt_index_menu (void)
  1801. mutt_curs_set (1); /* fallback from the pager */
  1802. }
  1803. +#ifdef USE_NNTP
  1804. + unset_option (OPTNEWS); /* for any case */
  1805. +#endif
  1806. switch (op)
  1807. {
  1808. @@ -760,6 +787,161 @@ int mutt_index_menu (void)
  1809. menu_current_bottom (menu);
  1810. break;
  1811. +#ifdef USE_NNTP
  1812. + case OP_GET_PARENT:
  1813. + CHECK_MSGCOUNT;
  1814. + CHECK_VISIBLE;
  1815. +
  1816. + case OP_GET_MESSAGE:
  1817. + CHECK_IN_MAILBOX;
  1818. + CHECK_READONLY;
  1819. + CHECK_ATTACH;
  1820. + if (Context->magic == M_NNTP)
  1821. + {
  1822. + HEADER *hdr;
  1823. +
  1824. + if (op == OP_GET_MESSAGE)
  1825. + {
  1826. + buf[0] = 0;
  1827. + if (mutt_get_field (_("Enter Message-Id: "),
  1828. + buf, sizeof (buf), 0) != 0 || !buf[0])
  1829. + break;
  1830. + }
  1831. + else
  1832. + {
  1833. + LIST *ref = CURHDR->env->references;
  1834. + if (!ref)
  1835. + {
  1836. + mutt_error _("Article has no parent reference.");
  1837. + break;
  1838. + }
  1839. + strfcpy (buf, ref->data, sizeof (buf));
  1840. + }
  1841. + if (!Context->id_hash)
  1842. + Context->id_hash = mutt_make_id_hash (Context);
  1843. + hdr = hash_find (Context->id_hash, buf);
  1844. + if (hdr)
  1845. + {
  1846. + if (hdr->virtual != -1)
  1847. + {
  1848. + menu->current = hdr->virtual;
  1849. + menu->redraw = REDRAW_MOTION_RESYNCH;
  1850. + }
  1851. + else if (hdr->collapsed)
  1852. + {
  1853. + mutt_uncollapse_thread (Context, hdr);
  1854. + mutt_set_virtual (Context);
  1855. + menu->current = hdr->virtual;
  1856. + menu->redraw = REDRAW_MOTION_RESYNCH;
  1857. + }
  1858. + else
  1859. + mutt_error _("Message is not visible in limited view.");
  1860. + }
  1861. + else
  1862. + {
  1863. + int rc;
  1864. +
  1865. + mutt_message (_("Fetching %s from server..."), buf);
  1866. + rc = nntp_check_msgid (Context, buf);
  1867. + if (rc == 0)
  1868. + {
  1869. + hdr = Context->hdrs[Context->msgcount - 1];
  1870. + mutt_sort_headers (Context, 0);
  1871. + menu->current = hdr->virtual;
  1872. + menu->redraw = REDRAW_FULL;
  1873. + }
  1874. + else if (rc > 0)
  1875. + mutt_error (_("Article %s not found on the server."), buf);
  1876. + }
  1877. + }
  1878. + break;
  1879. +
  1880. + case OP_GET_CHILDREN:
  1881. + case OP_RECONSTRUCT_THREAD:
  1882. + CHECK_MSGCOUNT;
  1883. + CHECK_VISIBLE;
  1884. + CHECK_READONLY;
  1885. + CHECK_ATTACH;
  1886. + if (Context->magic == M_NNTP)
  1887. + {
  1888. + int oldmsgcount = Context->msgcount;
  1889. + int oldindex = CURHDR->index;
  1890. + int rc = 0;
  1891. +
  1892. + if (!CURHDR->env->message_id)
  1893. + {
  1894. + mutt_error _("No Message-Id. Unable to perform operation.");
  1895. + break;
  1896. + }
  1897. +
  1898. + mutt_message _("Fetching message headers...");
  1899. + if (!Context->id_hash)
  1900. + Context->id_hash = mutt_make_id_hash (Context);
  1901. + strfcpy (buf, CURHDR->env->message_id, sizeof (buf));
  1902. +
  1903. + /* trying to find msgid of the root message */
  1904. + if (op == OP_RECONSTRUCT_THREAD)
  1905. + {
  1906. + LIST *ref = CURHDR->env->references;
  1907. + while (ref)
  1908. + {
  1909. + if (hash_find (Context->id_hash, ref->data) == NULL)
  1910. + {
  1911. + rc = nntp_check_msgid (Context, ref->data);
  1912. + if (rc < 0)
  1913. + break;
  1914. + }
  1915. +
  1916. + /* the last msgid in References is the root message */
  1917. + if (!ref->next)
  1918. + strfcpy (buf, ref->data, sizeof (buf));
  1919. + ref = ref->next;
  1920. + }
  1921. + }
  1922. +
  1923. + /* fetching all child messages */
  1924. + if (rc >= 0)
  1925. + rc = nntp_check_children (Context, buf);
  1926. +
  1927. + /* at least one message has been loaded */
  1928. + if (Context->msgcount > oldmsgcount)
  1929. + {
  1930. + HEADER *hdr;
  1931. + int i, quiet = Context->quiet;
  1932. +
  1933. + if (rc < 0)
  1934. + Context->quiet = 1;
  1935. + mutt_sort_headers (Context, (op == OP_RECONSTRUCT_THREAD));
  1936. + Context->quiet = quiet;
  1937. +
  1938. + /* if the root message was retrieved, move to it */
  1939. + hdr = hash_find (Context->id_hash, buf);
  1940. + if (hdr)
  1941. + menu->current = hdr->virtual;
  1942. +
  1943. + /* try to restore old position */
  1944. + else
  1945. + {
  1946. + for (i = 0; i < Context->msgcount; i++)
  1947. + {
  1948. + if (Context->hdrs[i]->index == oldindex)
  1949. + {
  1950. + menu->current = Context->hdrs[i]->virtual;
  1951. + /* as an added courtesy, recenter the menu
  1952. + * with the current entry at the middle of the screen */
  1953. + menu_check_recenter (menu);
  1954. + menu_current_middle (menu);
  1955. + }
  1956. + }
  1957. + }
  1958. + menu->redraw = REDRAW_FULL;
  1959. + }
  1960. + else if (rc >= 0)
  1961. + mutt_error _("No deleted messages found in the thread.");
  1962. + }
  1963. + break;
  1964. +#endif
  1965. +
  1966. case OP_JUMP:
  1967. CHECK_MSGCOUNT;
  1968. @@ -856,11 +1038,33 @@ int mutt_index_menu (void)
  1969. break;
  1970. case OP_MAIN_LIMIT:
  1971. + case OP_TOGGLE_READ:
  1972. CHECK_IN_MAILBOX;
  1973. menu->oldcurrent = (Context->vcount && menu->current >= 0 && menu->current < Context->vcount) ?
  1974. CURHDR->index : -1;
  1975. - if (mutt_pattern_func (M_LIMIT, _("Limit to messages matching: ")) == 0)
  1976. + if (op == OP_TOGGLE_READ)
  1977. + {
  1978. + char buf[LONG_STRING];
  1979. +
  1980. + if (!Context->pattern || strncmp (Context->pattern, "!~R!~D~s", 8) != 0)
  1981. + {
  1982. + snprintf (buf, sizeof (buf), "!~R!~D~s%s",
  1983. + Context->pattern ? Context->pattern : ".*");
  1984. + set_option (OPTHIDEREAD);
  1985. + }
  1986. + else
  1987. + {
  1988. + strfcpy (buf, Context->pattern + 8, sizeof(buf));
  1989. + if (!*buf || strncmp (buf, ".*", 2) == 0)
  1990. + snprintf (buf, sizeof(buf), "~A");
  1991. + unset_option (OPTHIDEREAD);
  1992. + }
  1993. + FREE (&Context->pattern);
  1994. + Context->pattern = safe_strdup (buf);
  1995. + }
  1996. + if ((op == OP_TOGGLE_READ && mutt_pattern_func (M_LIMIT, NULL) == 0) ||
  1997. + mutt_pattern_func (M_LIMIT, _("Limit to messages matching: ")) == 0)
  1998. {
  1999. if (menu->oldcurrent >= 0)
  2000. {
  2001. @@ -1103,15 +1307,22 @@ int mutt_index_menu (void)
  2002. case OP_SIDEBAR_OPEN:
  2003. case OP_MAIN_CHANGE_FOLDER:
  2004. case OP_MAIN_NEXT_UNREAD_MAILBOX:
  2005. -
  2006. - if (attach_msg)
  2007. - op = OP_MAIN_CHANGE_FOLDER_READONLY;
  2008. -
  2009. - /* fallback to the readonly case */
  2010. -
  2011. case OP_MAIN_CHANGE_FOLDER_READONLY:
  2012. +#ifdef USE_NNTP
  2013. + case OP_MAIN_CHANGE_GROUP:
  2014. + case OP_MAIN_CHANGE_GROUP_READONLY:
  2015. + unset_option (OPTNEWS);
  2016. +#endif
  2017. + if (attach_msg || option (OPTREADONLY) ||
  2018. +#ifdef USE_NNTP
  2019. + op == OP_MAIN_CHANGE_GROUP_READONLY ||
  2020. +#endif
  2021. + op == OP_MAIN_CHANGE_FOLDER_READONLY)
  2022. + flags = M_READONLY;
  2023. + else
  2024. + flags = 0;
  2025. - if ((op == OP_MAIN_CHANGE_FOLDER_READONLY) || option (OPTREADONLY))
  2026. + if (flags)
  2027. cp = _("Open mailbox in read-only mode");
  2028. else
  2029. cp = _("Open mailbox");
  2030. @@ -1130,6 +1341,22 @@ int mutt_index_menu (void)
  2031. }
  2032. else
  2033. {
  2034. +#ifdef USE_NNTP
  2035. + if (op == OP_MAIN_CHANGE_GROUP ||
  2036. + op == OP_MAIN_CHANGE_GROUP_READONLY)
  2037. + {
  2038. + set_option (OPTNEWS);
  2039. + CurrentNewsSrv = nntp_select_server (NewsServer, 0);
  2040. + if (!CurrentNewsSrv)
  2041. + break;
  2042. + if (flags)
  2043. + cp = _("Open newsgroup in read-only mode");
  2044. + else
  2045. + cp = _("Open newsgroup");
  2046. + nntp_buffy (buf, sizeof (buf));
  2047. + }
  2048. + else
  2049. +#endif
  2050. mutt_buffy (buf, sizeof (buf));
  2051. if ( op == OP_SIDEBAR_OPEN ) {
  2052. @@ -1153,6 +1380,14 @@ int mutt_index_menu (void)
  2053. }
  2054. }
  2055. +#ifdef USE_NNTP
  2056. + if (option (OPTNEWS))
  2057. + {
  2058. + unset_option (OPTNEWS);
  2059. + nntp_expand_path (buf, sizeof (buf), &CurrentNewsSrv->conn->account);
  2060. + }
  2061. + else
  2062. +#endif
  2063. mutt_expand_path (buf, sizeof (buf));
  2064. set_curbuffy(buf);
  2065. if (mx_get_magic (buf) <= 0)
  2066. @@ -1200,15 +1435,18 @@ int mutt_index_menu (void)
  2067. CurrentMenu = MENU_MAIN;
  2068. mutt_folder_hook (buf);
  2069. - if ((Context = mx_open_mailbox (buf,
  2070. - (option (OPTREADONLY) || op == OP_MAIN_CHANGE_FOLDER_READONLY) ?
  2071. - M_READONLY : 0, NULL)) != NULL)
  2072. + if ((Context = mx_open_mailbox (buf, flags, NULL)) != NULL)
  2073. {
  2074. menu->current = ci_first_message ();
  2075. }
  2076. else
  2077. menu->current = 0;
  2078. +#ifdef USE_NNTP
  2079. + /* mutt_buffy_check() must be done with mail-reader mode! */
  2080. + menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_MAIN,
  2081. + (Context && (Context->magic == M_NNTP)) ? IndexNewsHelp : IndexHelp);
  2082. +#endif
  2083. mutt_clear_error ();
  2084. mutt_buffy_check(1); /* force the buffy check after we have changed
  2085. the folder */
  2086. @@ -1277,6 +1515,7 @@ int mutt_index_menu (void)
  2087. CHECK_MSGCOUNT;
  2088. CHECK_VISIBLE;
  2089. CHECK_READONLY;
  2090. + CHECK_ACL(M_ACL_WRITE, _("break thread"));
  2091. if ((Sort & SORT_MASK) != SORT_THREADS)
  2092. mutt_error _("Threading is not enabled.");
  2093. @@ -1311,7 +1550,7 @@ int mutt_index_menu (void)
  2094. CHECK_MSGCOUNT;
  2095. CHECK_VISIBLE;
  2096. CHECK_READONLY;
  2097. - CHECK_ACL(M_ACL_DELETE, _("link threads"));
  2098. + CHECK_ACL(M_ACL_WRITE, _("link threads"));
  2099. if ((Sort & SORT_MASK) != SORT_THREADS)
  2100. mutt_error _("Threading is not enabled.");
  2101. @@ -1932,6 +2171,20 @@ int mutt_index_menu (void)
  2102. }
  2103. break;
  2104. +#ifdef USE_NNTP
  2105. + case OP_CATCHUP:
  2106. + CHECK_MSGCOUNT;
  2107. + CHECK_READONLY;
  2108. + CHECK_ATTACH
  2109. + if (Context && Context->magic == M_NNTP)
  2110. + {
  2111. + NNTP_DATA *nntp_data = Context->data;
  2112. + if (mutt_newsgroup_catchup (nntp_data->nserv, nntp_data->group))
  2113. + menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
  2114. + }
  2115. + break;
  2116. +#endif
  2117. +
  2118. case OP_DISPLAY_ADDRESS:
  2119. CHECK_MSGCOUNT;
  2120. @@ -2136,6 +2389,39 @@ int mutt_index_menu (void)
  2121. menu->redraw = REDRAW_FULL;
  2122. break;
  2123. +#ifdef USE_NNTP
  2124. + case OP_FOLLOWUP:
  2125. + case OP_FORWARD_TO_GROUP:
  2126. +
  2127. + CHECK_MSGCOUNT;
  2128. + CHECK_VISIBLE;
  2129. +
  2130. + case OP_POST:
  2131. +
  2132. + CHECK_ATTACH;
  2133. + if (op != OP_FOLLOWUP || !CURHDR->env->followup_to ||
  2134. + mutt_strcasecmp (CURHDR->env->followup_to, "poster") ||
  2135. + query_quadoption (OPT_FOLLOWUPTOPOSTER,
  2136. + _("Reply by mail as poster prefers?")) != M_YES)
  2137. + {
  2138. + if (Context && Context->magic == M_NNTP &&
  2139. + !((NNTP_DATA *)Context->data)->allowed &&
  2140. + query_quadoption (OPT_TOMODERATED,
  2141. + _("Posting to this group not allowed, may be moderated. Continue?")) != M_YES)
  2142. + break;
  2143. + if (op == OP_POST)
  2144. + ci_send_message (SENDNEWS, NULL, NULL, Context, NULL);
  2145. + else
  2146. + {
  2147. + CHECK_MSGCOUNT;
  2148. + ci_send_message ((op == OP_FOLLOWUP ? SENDREPLY : SENDFORWARD) |
  2149. + SENDNEWS, NULL, NULL, Context, tag ? NULL : CURHDR);
  2150. + }
  2151. + menu->redraw = REDRAW_FULL;
  2152. + break;
  2153. + }
  2154. +#endif
  2155. +
  2156. case OP_REPLY:
  2157. CHECK_ATTACH;
  2158. diff --git a/doc/Muttrc b/doc/Muttrc
  2159. index bf0e6d0..e03da88 100644
  2160. --- a/doc/Muttrc
  2161. +++ b/doc/Muttrc
  2162. @@ -240,6 +240,28 @@ attachments -I message/external-body
  2163. # editing the body of an outgoing message.
  2164. #
  2165. #
  2166. +# set ask_follow_up=no
  2167. +#
  2168. +# Name: ask_follow_up
  2169. +# Type: boolean
  2170. +# Default: no
  2171. +#
  2172. +#
  2173. +# If set, Mutt will prompt you for follow-up groups before editing
  2174. +# the body of an outgoing message.
  2175. +#
  2176. +#
  2177. +# set ask_x_comment_to=no
  2178. +#
  2179. +# Name: ask_x_comment_to
  2180. +# Type: boolean
  2181. +# Default: no
  2182. +#
  2183. +#
  2184. +# If set, Mutt will prompt you for x-comment-to field before editing
  2185. +# the body of an outgoing message.
  2186. +#
  2187. +#
  2188. # set assumed_charset=""
  2189. #
  2190. # Name: assumed_charset
  2191. @@ -445,6 +467,17 @@ attachments -I message/external-body
  2192. # visual terminals don't permit making the cursor invisible.
  2193. #
  2194. #
  2195. +# set catchup_newsgroup=ask-yes
  2196. +#
  2197. +# Name: catchup_newsgroup
  2198. +# Type: quadoption
  2199. +# Default: ask-yes
  2200. +#
  2201. +#
  2202. +# If this variable is set, Mutt will mark all articles in newsgroup
  2203. +# as read when you quit the newsgroup (catchup newsgroup).
  2204. +#
  2205. +#
  2206. # set certificate_file="~/.mutt_certificates"
  2207. #
  2208. # Name: certificate_file
  2209. @@ -1131,6 +1164,19 @@ attachments -I message/external-body
  2210. # of the same email for you.
  2211. #
  2212. #
  2213. +# set followup_to_poster=ask-yes
  2214. +#
  2215. +# Name: followup_to_poster
  2216. +# Type: quadoption
  2217. +# Default: ask-yes
  2218. +#
  2219. +#
  2220. +# If this variable is set and the keyword "poster" is present in
  2221. +# Followup-To header, follow-up to newsgroup function is not
  2222. +# permitted. The message will be mailed to the submitter of the
  2223. +# message via mail.
  2224. +#
  2225. +#
  2226. # set force_name=no
  2227. #
  2228. # Name: force_name
  2229. @@ -1241,6 +1287,27 @@ attachments -I message/external-body
  2230. # ``Franklin'' to ``Franklin, Steve''.
  2231. #
  2232. #
  2233. +# set group_index_format="%4C %M%N %5s %-45.45f %d"
  2234. +#
  2235. +# Name: group_index_format
  2236. +# Type: string
  2237. +# Default: "%4C %M%N %5s %-45.45f %d"
  2238. +#
  2239. +#
  2240. +# This variable allows you to customize the newsgroup browser display to
  2241. +# your personal taste. This string is similar to ``index_format'', but
  2242. +# has its own set of printf()-like sequences:
  2243. +# %C current newsgroup number
  2244. +# %d description of newsgroup (becomes from server)
  2245. +# %f newsgroup name
  2246. +# %M - if newsgroup not allowed for direct post (moderated for example)
  2247. +# %N N if newsgroup is new, u if unsubscribed, blank otherwise
  2248. +# %n number of new articles in newsgroup
  2249. +# %s number of unread articles in newsgroup
  2250. +# %>X right justify the rest of the string and pad with character "X"
  2251. +# %|X pad to the end of the line with character "X"
  2252. +#
  2253. +#
  2254. # set hdrs=yes
  2255. #
  2256. # Name: hdrs
  2257. @@ -1789,6 +1856,7 @@ attachments -I message/external-body
  2258. # %E number of messages in current thread
  2259. # %f sender (address + real name), either From: or Return-Path:
  2260. # %F author name, or recipient name if the message is from you
  2261. +# %g newsgroup name (if compiled with NNTP support)
  2262. # %H spam attribute(s) of this message
  2263. # %i message-id of the current message
  2264. # %l number of lines in the message (does not work with maildir,
  2265. @@ -1804,12 +1872,14 @@ attachments -I message/external-body
  2266. # stashed the message: list name or recipient name
  2267. # if not sent to a list
  2268. # %P progress indicator for the built-in pager (how much of the file has been displayed)
  2269. +# %R ``X-Comment-To:'' field (if present and compiled with NNTP support)
  2270. # %s subject of the message
  2271. # %S status of the message (``N''/``D''/``d''/``!''/``r''/*)
  2272. # %t ``To:'' field (recipients)
  2273. # %T the appropriate character from the $to_chars string
  2274. # %u user (login) name of the author
  2275. # %v first name of the author, or the recipient if the message is from you
  2276. +# %W name of organization of author (``Organization:'' field)
  2277. # %X number of attachments
  2278. # (please see the ``attachments'' section for possible speed effects)
  2279. # %y ``X-Label:'' field, if present
  2280. @@ -1845,6 +1915,27 @@ attachments -I message/external-body
  2281. # ``save-hook'', ``fcc-hook'' and ``fcc-save-hook'', too.
  2282. #
  2283. #
  2284. +# set inews=""
  2285. +#
  2286. +# Name: inews
  2287. +# Type: path
  2288. +# Default: ""
  2289. +#
  2290. +#
  2291. +# If set, specifies the program and arguments used to deliver news posted
  2292. +# by Mutt. Otherwise, mutt posts article using current connection to
  2293. +# news server. The following printf-style sequence is understood:
  2294. +# %a account url
  2295. +# %p port
  2296. +# %P port if specified
  2297. +# %s news server name
  2298. +# %S url schema
  2299. +# %u username
  2300. +#
  2301. +#
  2302. +# Example: set inews="/usr/local/bin/inews -hS"
  2303. +#
  2304. +#
  2305. # set ispell="ispell"
  2306. #
  2307. # Name: ispell
  2308. @@ -2214,6 +2305,18 @@ attachments -I message/external-body
  2309. # be attached to the newly composed message if this option is set.
  2310. #
  2311. #
  2312. +# set mime_subject=yes
  2313. +#
  2314. +# Name: mime_subject
  2315. +# Type: boolean
  2316. +# Default: yes
  2317. +#
  2318. +#
  2319. +# If unset, 8-bit ``subject:'' line in article header will not be
  2320. +# encoded according to RFC2047 to base64. This is useful when message
  2321. +# is Usenet article, because MIME for news is nonstandard feature.
  2322. +#
  2323. +#
  2324. # set mix_entry_format="%4n %c %-16s %a"
  2325. #
  2326. # Name: mix_entry_format
  2327. @@ -2280,6 +2383,144 @@ attachments -I message/external-body
  2328. # See also $read_inc, $write_inc and $net_inc.
  2329. #
  2330. #
  2331. +# set news_cache_dir="~/.mutt"
  2332. +#
  2333. +# Name: news_cache_dir
  2334. +# Type: path
  2335. +# Default: "~/.mutt"
  2336. +#
  2337. +#
  2338. +# This variable pointing to directory where Mutt will save cached news
  2339. +# articles and headers in. If unset, articles and headers will not be
  2340. +# saved at all and will be reloaded from the server each time.
  2341. +#
  2342. +#
  2343. +# set news_server=""
  2344. +#
  2345. +# Name: news_server
  2346. +# Type: string
  2347. +# Default: ""
  2348. +#
  2349. +#
  2350. +# This variable specifies domain name or address of NNTP server. It
  2351. +# defaults to the news server specified in the environment variable
  2352. +# $NNTPSERVER or contained in the file /etc/nntpserver. You can also
  2353. +# specify username and an alternative port for each news server, ie:
  2354. +#
  2355. +# [[s]news://][username[:password]@]server[:port]
  2356. +#
  2357. +#
  2358. +# set newsgroups_charset="utf-8"
  2359. +#
  2360. +# Name: newsgroups_charset
  2361. +# Type: string
  2362. +# Default: "utf-8"
  2363. +#
  2364. +#
  2365. +# Character set of newsgroups descriptions.
  2366. +#
  2367. +#
  2368. +# set newsrc="~/.newsrc"
  2369. +#
  2370. +# Name: newsrc
  2371. +# Type: path
  2372. +# Default: "~/.newsrc"
  2373. +#
  2374. +#
  2375. +# The file, containing info about subscribed newsgroups - names and
  2376. +# indexes of read articles. The following printf-style sequence
  2377. +# is understood:
  2378. +# %a account url
  2379. +# %p port
  2380. +# %P port if specified
  2381. +# %s news server name
  2382. +# %S url schema
  2383. +# %u username
  2384. +#
  2385. +#
  2386. +# set nntp_authenticators=""
  2387. +#
  2388. +# Name: nntp_authenticators
  2389. +# Type: string
  2390. +# Default: ""
  2391. +#
  2392. +#
  2393. +# This is a colon-delimited list of authentication methods mutt may
  2394. +# attempt to use to log in to a news server, in the order mutt should
  2395. +# try them. Authentication methods are either ``user'' or any
  2396. +# SASL mechanism, e.g. ``digest-md5'', ``gssapi'' or ``cram-md5''.
  2397. +# This option is case-insensitive. If it's unset (the default)
  2398. +# mutt will try all available methods, in order from most-secure to
  2399. +# least-secure.
  2400. +#
  2401. +# Example:
  2402. +# set nntp_authenticators="digest-md5:user"
  2403. +#
  2404. +# Note: Mutt will only fall back to other authentication methods if
  2405. +# the previous methods are unavailable. If a method is available but
  2406. +# authentication fails, mutt will not connect to the IMAP server.
  2407. +#
  2408. +#
  2409. +# set nntp_context=1000
  2410. +#
  2411. +# Name: nntp_context
  2412. +# Type: number
  2413. +# Default: 1000
  2414. +#
  2415. +#
  2416. +# This variable defines number of articles which will be in index when
  2417. +# newsgroup entered. If active newsgroup have more articles than this
  2418. +# number, oldest articles will be ignored. Also controls how many
  2419. +# articles headers will be saved in cache when you quit newsgroup.
  2420. +#
  2421. +#
  2422. +# set nntp_load_description=yes
  2423. +#
  2424. +# Name: nntp_load_description
  2425. +# Type: boolean
  2426. +# Default: yes
  2427. +#
  2428. +#
  2429. +# This variable controls whether or not descriptions for each newsgroup
  2430. +# must be loaded when newsgroup is added to list (first time list
  2431. +# loading or new newsgroup adding).
  2432. +#
  2433. +#
  2434. +# set nntp_user=""
  2435. +#
  2436. +# Name: nntp_user
  2437. +# Type: string
  2438. +# Default: ""
  2439. +#
  2440. +#
  2441. +# Your login name on the NNTP server. If unset and NNTP server requires
  2442. +# authentification, Mutt will prompt you for your account name when you
  2443. +# connect to news server.
  2444. +#
  2445. +#
  2446. +# set nntp_pass=""
  2447. +#
  2448. +# Name: nntp_pass
  2449. +# Type: string
  2450. +# Default: ""
  2451. +#
  2452. +#
  2453. +# Your password for NNTP account.
  2454. +#
  2455. +#
  2456. +# set nntp_poll=60
  2457. +#
  2458. +# Name: nntp_poll
  2459. +# Type: number
  2460. +# Default: 60
  2461. +#
  2462. +#
  2463. +# The time in seconds until any operations on newsgroup except post new
  2464. +# article will cause recheck for new news. If set to 0, Mutt will
  2465. +# recheck newsgroup on each operation in index (stepping, read article,
  2466. +# etc.).
  2467. +#
  2468. +#
  2469. # set pager="builtin"
  2470. #
  2471. # Name: pager
  2472. @@ -2997,6 +3238,19 @@ attachments -I message/external-body
  2473. # string after the inclusion of a message which is being replied to.
  2474. #
  2475. #
  2476. +# set post_moderated=ask-yes
  2477. +#
  2478. +# Name: post_moderated
  2479. +# Type: quadoption
  2480. +# Default: ask-yes
  2481. +#
  2482. +#
  2483. +# If set to yes, Mutt will post article to newsgroup that have
  2484. +# not permissions to posting (e.g. moderated). Note: if news server
  2485. +# does not support posting to that newsgroup or totally read-only, that
  2486. +# posting will not have an effect.
  2487. +#
  2488. +#
  2489. # set postpone=ask-yes
  2490. #
  2491. # Name: postpone
  2492. @@ -3605,6 +3859,41 @@ attachments -I message/external-body
  2493. # shell from /etc/passwd is used.
  2494. #
  2495. #
  2496. +# set save_unsubscribed=no
  2497. +#
  2498. +# Name: save_unsubscribed
  2499. +# Type: boolean
  2500. +# Default: no
  2501. +#
  2502. +#
  2503. +# When set, info about unsubscribed newsgroups will be saved into
  2504. +# ``newsrc'' file and into cache.
  2505. +#
  2506. +#
  2507. +# set show_new_news=yes
  2508. +#
  2509. +# Name: show_new_news
  2510. +# Type: boolean
  2511. +# Default: yes
  2512. +#
  2513. +#
  2514. +# If set, news server will be asked for new newsgroups on entering
  2515. +# the browser. Otherwise, it will be done only once for a news server.
  2516. +# Also controls whether or not number of new articles of subscribed
  2517. +# newsgroups will be then checked.
  2518. +#
  2519. +#
  2520. +# set show_only_unread=no
  2521. +#
  2522. +# Name: show_only_unread
  2523. +# Type: boolean
  2524. +# Default: no
  2525. +#
  2526. +#
  2527. +# If set, only subscribed newsgroups that contain unread articles
  2528. +# will be displayed in browser.
  2529. +#
  2530. +#
  2531. # set sig_dashes=yes
  2532. #
  2533. # Name: sig_dashes
  2534. @@ -4851,3 +5140,14 @@ attachments -I message/external-body
  2535. # ``tuning'' section of the manual for performance considerations.
  2536. #
  2537. #
  2538. +# set x_comment_to=no
  2539. +#
  2540. +# Name: x_comment_to
  2541. +# Type: boolean
  2542. +# Default: no
  2543. +#
  2544. +#
  2545. +# If set, Mutt will add ``X-Comment-To:'' field (that contains full
  2546. +# name of original article author) to article that followuped to newsgroup.
  2547. +#
  2548. +#
  2549. diff --git a/doc/manual.xml.head b/doc/manual.xml.head
  2550. index 4366758..5b523ce 100644
  2551. --- a/doc/manual.xml.head
  2552. +++ b/doc/manual.xml.head
  2553. @@ -1694,6 +1694,26 @@ See also the <link linkend="postpone">$postpone</link> quad-option.
  2554. </sect1>
  2555. +<sect1 id="nntp">
  2556. +<title>Reading news via NNTP</title>
  2557. +
  2558. +<para>
  2559. +If compiled with <emphasis>--enable-nntp</emphasis> option, Mutt can
  2560. +read news from news server via NNTP. You can open a newsgroup with
  2561. +function ``change-newsgroup'' (default: ``i''). Default news server
  2562. +can be obtained from <literal>$NNTPSERVER</literal> environment
  2563. +variable or from <literal>/etc/nntpserver</literal> file. Like other
  2564. +news readers, info about subscribed newsgroups is saved in file by
  2565. +<link linkend="newsrc">$newsrc</link> variable. The variable <link
  2566. +linkend="news-cache-dir">$news_cache_dir</link> can be used to point
  2567. +to a directory. Mutt will create a hierarchy of subdirectories named
  2568. +like the account and newsgroup the cache is for. Also the hierarchy
  2569. +is used to store header cache if Mutt was compiled with <link
  2570. +linkend="header-caching">header cache</link> support.
  2571. +</para>
  2572. +
  2573. +</sect1>
  2574. +
  2575. </chapter>
  2576. <chapter id="configuration">
  2577. diff --git a/doc/mutt.man b/doc/mutt.man
  2578. index 718f87a..c4f44d1 100644
  2579. --- a/doc/mutt.man
  2580. +++ b/doc/mutt.man
  2581. @@ -23,8 +23,8 @@ mutt \- The Mutt Mail User Agent
  2582. .SH SYNOPSIS
  2583. .PP
  2584. .B mutt
  2585. -[\-nRyzZ]
  2586. -[\-e \fIcmd\fP] [\-F \fIfile\fP] [\-m \fItype\fP] [\-f \fIfile\fP]
  2587. +[\-GnRyzZ]
  2588. +[\-e \fIcmd\fP] [\-F \fIfile\fP] [\-g \fIserver\fP] [\-m \fItype\fP] [\-f \fIfile\fP]
  2589. .PP
  2590. .B mutt
  2591. [\-nx]
  2592. @@ -101,6 +101,10 @@ files.
  2593. Specify which mailbox to load.
  2594. .IP "-F \fImuttrc\fP"
  2595. Specify an initialization file to read instead of ~/.muttrc
  2596. +.IP "-g \fIserver\fP"
  2597. +Start Mutt with a listing of subscribed newsgroups at specified news server.
  2598. +.IP "-G"
  2599. +Start Mutt with a listing of subscribed newsgroups.
  2600. .IP "-h"
  2601. Display help.
  2602. .IP "-H \fIdraft\fP"
  2603. diff --git a/functions.h b/functions.h
  2604. index 363b4d5..66f3345 100644
  2605. --- a/functions.h
  2606. +++ b/functions.h
  2607. @@ -88,6 +88,10 @@ const struct binding_t OpMain[] = { /* map: index */
  2608. { "break-thread", OP_MAIN_BREAK_THREAD, "#" },
  2609. { "change-folder", OP_MAIN_CHANGE_FOLDER, "c" },
  2610. { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" },
  2611. +#ifdef USE_NNTP
  2612. + { "change-newsgroup", OP_MAIN_CHANGE_GROUP, "i" },
  2613. + { "change-newsgroup-readonly",OP_MAIN_CHANGE_GROUP_READONLY, "\033i" },
  2614. +#endif
  2615. { "next-unread-mailbox", OP_MAIN_NEXT_UNREAD_MAILBOX, NULL },
  2616. { "collapse-thread", OP_MAIN_COLLAPSE_THREAD, "\033v" },
  2617. { "collapse-all", OP_MAIN_COLLAPSE_ALL, "\033V" },
  2618. @@ -101,7 +105,15 @@ const struct binding_t OpMain[] = { /* map: index */
  2619. { "edit", OP_EDIT_MESSAGE, "e" },
  2620. { "edit-type", OP_EDIT_TYPE, "\005" },
  2621. { "forward-message", OP_FORWARD_MESSAGE, "f" },
  2622. - { "flag-message", OP_FLAG_MESSAGE, "F" },
  2623. +#ifdef USE_NNTP
  2624. + { "forward-to-group", OP_FORWARD_TO_GROUP, "\033F" },
  2625. + { "followup-message", OP_FOLLOWUP, "F" },
  2626. + { "get-children", OP_GET_CHILDREN, NULL },
  2627. + { "get-message", OP_GET_MESSAGE, "\007" },
  2628. + { "get-parent", OP_GET_PARENT, "\033G" },
  2629. + { "reconstruct-thread", OP_RECONSTRUCT_THREAD, NULL },
  2630. +#endif
  2631. + { "flag-message", OP_FLAG_MESSAGE, "\033f" },
  2632. { "group-reply", OP_GROUP_REPLY, "g" },
  2633. #ifdef USE_POP
  2634. { "fetch-mail", OP_MAIN_FETCH_MAIL, "G" },
  2635. @@ -129,6 +141,9 @@ const struct binding_t OpMain[] = { /* map: index */
  2636. { "sort-mailbox", OP_SORT, "o" },
  2637. { "sort-reverse", OP_SORT_REVERSE, "O" },
  2638. { "print-message", OP_PRINT, "p" },
  2639. +#ifdef USE_NNTP
  2640. + { "post-message", OP_POST, "P" },
  2641. +#endif
  2642. { "previous-thread", OP_MAIN_PREV_THREAD, "\020" },
  2643. { "previous-subthread", OP_MAIN_PREV_SUBTHREAD, "\033p" },
  2644. { "recall-message", OP_RECALL_MESSAGE, "R" },
  2645. @@ -148,6 +163,10 @@ const struct binding_t OpMain[] = { /* map: index */
  2646. { "show-version", OP_VERSION, "V" },
  2647. { "set-flag", OP_MAIN_SET_FLAG, "w" },
  2648. { "clear-flag", OP_MAIN_CLEAR_FLAG, "W" },
  2649. + { "toggle-read", OP_TOGGLE_READ, "X" },
  2650. +#ifdef USE_NNTP
  2651. + { "catchup", OP_CATCHUP, "y" },
  2652. +#endif
  2653. { "display-message", OP_DISPLAY_MESSAGE, M_ENTER_S },
  2654. { "buffy-list", OP_BUFFY_LIST, "." },
  2655. { "sync-mailbox", OP_MAIN_SYNC_FOLDER, "$" },
  2656. @@ -159,7 +178,7 @@ const struct binding_t OpMain[] = { /* map: index */
  2657. { "previous-new-then-unread", OP_MAIN_PREV_NEW_THEN_UNREAD, "\033\t" },
  2658. { "next-unread", OP_MAIN_NEXT_UNREAD, NULL },
  2659. { "previous-unread", OP_MAIN_PREV_UNREAD, NULL },
  2660. - { "parent-message", OP_MAIN_PARENT_MESSAGE, "P" },
  2661. + { "parent-message", OP_MAIN_PARENT_MESSAGE, NULL },
  2662. { "extract-keys", OP_EXTRACT_KEYS, "\013" },
  2663. @@ -186,6 +205,10 @@ const struct binding_t OpPager[] = { /* map: pager */
  2664. { "bounce-message", OP_BOUNCE_MESSAGE, "b" },
  2665. { "change-folder", OP_MAIN_CHANGE_FOLDER, "c" },
  2666. { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" },
  2667. +#ifdef USE_NNTP
  2668. + { "change-newsgroup", OP_MAIN_CHANGE_GROUP, "i" },
  2669. + { "change-newsgroup-readonly",OP_MAIN_CHANGE_GROUP_READONLY, "\033i" },
  2670. +#endif
  2671. { "next-unread-mailbox", OP_MAIN_NEXT_UNREAD_MAILBOX, NULL },
  2672. { "copy-message", OP_COPY_MESSAGE, "C" },
  2673. { "decode-copy", OP_DECODE_COPY, "\033C" },
  2674. @@ -196,8 +219,12 @@ const struct binding_t OpPager[] = { /* map: pager */
  2675. { "clear-flag", OP_MAIN_CLEAR_FLAG, "W" },
  2676. { "edit", OP_EDIT_MESSAGE, "e" },
  2677. { "edit-type", OP_EDIT_TYPE, "\005" },
  2678. +#ifdef USE_NNTP
  2679. + { "followup-message", OP_FOLLOWUP, "F" },
  2680. + { "forward-to-group", OP_FORWARD_TO_GROUP, "\033F" },
  2681. +#endif
  2682. { "forward-message", OP_FORWARD_MESSAGE, "f" },
  2683. - { "flag-message", OP_FLAG_MESSAGE, "F" },
  2684. + { "flag-message", OP_FLAG_MESSAGE, "\033f" },
  2685. { "group-reply", OP_GROUP_REPLY, "g" },
  2686. #ifdef USE_IMAP
  2687. { "imap-fetch-mail", OP_MAIN_IMAP_FETCH, NULL },
  2688. @@ -219,6 +246,9 @@ const struct binding_t OpPager[] = { /* map: pager */
  2689. { "sort-mailbox", OP_SORT, "o" },
  2690. { "sort-reverse", OP_SORT_REVERSE, "O" },
  2691. { "print-message", OP_PRINT, "p" },
  2692. +#ifdef USE_NNTP
  2693. + { "post-message", OP_POST, "P" },
  2694. +#endif
  2695. { "previous-thread", OP_MAIN_PREV_THREAD, "\020" },
  2696. { "previous-subthread",OP_MAIN_PREV_SUBTHREAD, "\033p" },
  2697. { "purge-message", OP_PURGE_MESSAGE, NULL },
  2698. @@ -267,7 +297,7 @@ const struct binding_t OpPager[] = { /* map: pager */
  2699. { "half-down", OP_HALF_DOWN, NULL },
  2700. { "previous-line", OP_PREV_LINE, NULL },
  2701. { "bottom", OP_PAGER_BOTTOM, NULL },
  2702. - { "parent-message", OP_MAIN_PARENT_MESSAGE, "P" },
  2703. + { "parent-message", OP_MAIN_PARENT_MESSAGE, NULL },
  2704. @@ -295,6 +325,10 @@ const struct binding_t OpAttach[] = { /* map: attachment */
  2705. { "bounce-message", OP_BOUNCE_MESSAGE, "b" },
  2706. { "display-toggle-weed", OP_DISPLAY_HEADERS, "h" },
  2707. { "edit-type", OP_EDIT_TYPE, "\005" },
  2708. +#ifdef USE_NNTP
  2709. + { "followup-message", OP_FOLLOWUP, "F" },
  2710. + { "forward-to-group", OP_FORWARD_TO_GROUP, "\033F" },
  2711. +#endif
  2712. { "print-entry", OP_PRINT, "p" },
  2713. { "save-entry", OP_SAVE, "s" },
  2714. { "pipe-entry", OP_PIPE, "|" },
  2715. @@ -320,6 +354,7 @@ const struct binding_t OpAttach[] = { /* map: attachment */
  2716. const struct binding_t OpCompose[] = { /* map: compose */
  2717. { "attach-file", OP_COMPOSE_ATTACH_FILE, "a" },
  2718. { "attach-message", OP_COMPOSE_ATTACH_MESSAGE, "A" },
  2719. + { "attach-news-message",OP_COMPOSE_ATTACH_NEWS_MESSAGE,"\033a" },
  2720. { "edit-bcc", OP_COMPOSE_EDIT_BCC, "b" },
  2721. { "edit-cc", OP_COMPOSE_EDIT_CC, "c" },
  2722. { "copy-file", OP_SAVE, "C" },
  2723. @@ -339,6 +374,11 @@ const struct binding_t OpCompose[] = { /* map: compose */
  2724. { "print-entry", OP_PRINT, "l" },
  2725. { "edit-mime", OP_COMPOSE_EDIT_MIME, "m" },
  2726. { "new-mime", OP_COMPOSE_NEW_MIME, "n" },
  2727. +#ifdef USE_NNTP
  2728. + { "edit-newsgroups", OP_COMPOSE_EDIT_NEWSGROUPS, "N" },
  2729. + { "edit-followup-to", OP_COMPOSE_EDIT_FOLLOWUP_TO, "o" },
  2730. + { "edit-x-comment-to",OP_COMPOSE_EDIT_X_COMMENT_TO, "x" },
  2731. +#endif
  2732. { "postpone-message", OP_COMPOSE_POSTPONE_MESSAGE, "P" },
  2733. { "edit-reply-to", OP_COMPOSE_EDIT_REPLY_TO, "r" },
  2734. { "rename-file", OP_COMPOSE_RENAME_FILE, "R" },
  2735. @@ -390,14 +430,25 @@ const struct binding_t OpBrowser[] = { /* map: browser */
  2736. { "select-new", OP_BROWSER_NEW_FILE, "N" },
  2737. { "check-new", OP_CHECK_NEW, NULL },
  2738. { "toggle-mailboxes", OP_TOGGLE_MAILBOXES, "\t" },
  2739. +#ifdef USE_NNTP
  2740. + { "reload-active", OP_LOAD_ACTIVE, "g" },
  2741. + { "subscribe-pattern", OP_SUBSCRIBE_PATTERN, "S" },
  2742. + { "unsubscribe-pattern", OP_UNSUBSCRIBE_PATTERN, "U" },
  2743. + { "catchup", OP_CATCHUP, "y" },
  2744. + { "uncatchup", OP_UNCATCHUP, "Y" },
  2745. +#endif
  2746. { "view-file", OP_BROWSER_VIEW_FILE, " " },
  2747. { "buffy-list", OP_BUFFY_LIST, "." },
  2748. #ifdef USE_IMAP
  2749. { "create-mailbox", OP_CREATE_MAILBOX, "C" },
  2750. { "delete-mailbox", OP_DELETE_MAILBOX, "d" },
  2751. { "rename-mailbox", OP_RENAME_MAILBOX, "r" },
  2752. +#endif
  2753. +#if defined USE_IMAP || defined USE_NNTP
  2754. { "subscribe", OP_BROWSER_SUBSCRIBE, "s" },
  2755. { "unsubscribe", OP_BROWSER_UNSUBSCRIBE, "u" },
  2756. +#endif
  2757. +#ifdef USE_IMAP
  2758. { "toggle-subscribed", OP_BROWSER_TOGGLE_LSUB, "T" },
  2759. #endif
  2760. { NULL, 0, NULL }
  2761. diff --git a/globals.h b/globals.h
  2762. index 61765a4..0529f94 100644
  2763. --- a/globals.h
  2764. +++ b/globals.h
  2765. @@ -95,6 +95,17 @@ WHERE char *MixEntryFormat;
  2766. #endif
  2767. WHERE char *Muttrc INITVAL (NULL);
  2768. +#ifdef USE_NNTP
  2769. +WHERE char *GroupFormat;
  2770. +WHERE char *Inews;
  2771. +WHERE char *NewsCacheDir;
  2772. +WHERE char *NewsServer;
  2773. +WHERE char *NewsgroupsCharset;
  2774. +WHERE char *NewsRc;
  2775. +WHERE char *NntpAuthenticators;
  2776. +WHERE char *NntpUser;
  2777. +WHERE char *NntpPass;
  2778. +#endif
  2779. WHERE char *Outbox;
  2780. WHERE char *Pager;
  2781. WHERE char *PagerFmt;
  2782. @@ -193,6 +204,11 @@ extern unsigned char QuadOptions[];
  2783. WHERE unsigned short Counter INITVAL (0);
  2784. +#ifdef USE_NNTP
  2785. +WHERE short NewsPollTimeout;
  2786. +WHERE short NntpContext;
  2787. +#endif
  2788. +
  2789. WHERE short ConnectTimeout;
  2790. WHERE short HistSize;
  2791. WHERE short MenuContext;
  2792. diff --git a/hash.c b/hash.c
  2793. index 08f7171..983a7fd 100644
  2794. --- a/hash.c
  2795. +++ b/hash.c
  2796. @@ -57,6 +57,7 @@ HASH *hash_create (int nelem, int lower)
  2797. if (nelem == 0)
  2798. nelem = 2;
  2799. table->nelem = nelem;
  2800. + table->curnelem = 0;
  2801. table->table = safe_calloc (nelem, sizeof (struct hash_elem *));
  2802. if (lower)
  2803. {
  2804. @@ -71,6 +72,29 @@ HASH *hash_create (int nelem, int lower)
  2805. return table;
  2806. }
  2807. +HASH *hash_resize (HASH *ptr, int nelem, int lower)
  2808. +{
  2809. + HASH *table;
  2810. + struct hash_elem *elem, *tmp;
  2811. + int i;
  2812. +
  2813. + table = hash_create (nelem, lower);
  2814. +
  2815. + for (i = 0; i < ptr->nelem; i++)
  2816. + {
  2817. + for (elem = ptr->table[i]; elem; )
  2818. + {
  2819. + tmp = elem;
  2820. + elem = elem->next;
  2821. + hash_insert (table, tmp->key, tmp->data, 1);
  2822. + FREE (&tmp);
  2823. + }
  2824. + }
  2825. + FREE (&ptr->table);
  2826. + FREE (&ptr);
  2827. + return table;
  2828. +}
  2829. +
  2830. /* table hash table to update
  2831. * key key to hash on
  2832. * data data to associate with `key'
  2833. @@ -90,6 +114,7 @@ int hash_insert (HASH * table, const char *key, void *data, int allow_dup)
  2834. {
  2835. ptr->next = table->table[h];
  2836. table->table[h] = ptr;
  2837. + table->curnelem++;
  2838. }
  2839. else
  2840. {
  2841. @@ -112,6 +137,7 @@ int hash_insert (HASH * table, const char *key, void *data, int allow_dup)
  2842. else
  2843. table->table[h] = ptr;
  2844. ptr->next = tmp;
  2845. + table->curnelem++;
  2846. }
  2847. return h;
  2848. }
  2849. @@ -142,6 +168,7 @@ void hash_delete_hash (HASH * table, int hash, const char *key, const void *data
  2850. if (destroy)
  2851. destroy (ptr->data);
  2852. FREE (&ptr);
  2853. + table->curnelem--;
  2854. ptr = *last;
  2855. }
  2856. diff --git a/hash.h b/hash.h
  2857. index fb77d0c..9e7df82 100644
  2858. --- a/hash.h
  2859. +++ b/hash.h
  2860. @@ -28,7 +28,7 @@ struct hash_elem
  2861. typedef struct
  2862. {
  2863. - int nelem;
  2864. + int nelem, curnelem;
  2865. struct hash_elem **table;
  2866. unsigned int (*hash_string)(const unsigned char *, unsigned int);
  2867. int (*cmp_string)(const char *, const char *);
  2868. @@ -41,6 +41,7 @@ HASH;
  2869. HASH *hash_create (int nelem, int lower);
  2870. int hash_insert (HASH * table, const char *key, void *data, int allow_dup);
  2871. +HASH *hash_resize (HASH * table, int nelem, int lower);
  2872. void *hash_find_hash (const HASH * table, int hash, const char *key);
  2873. void hash_delete_hash (HASH * table, int hash, const char *key, const void *data,
  2874. void (*destroy) (void *));
  2875. diff --git a/hcache.c b/hcache.c
  2876. index af17932..555e1d1 100644
  2877. --- a/hcache.c
  2878. +++ b/hcache.c
  2879. @@ -447,6 +447,12 @@ dump_envelope(ENVELOPE * e, unsigned char *d, int *off, int convert)
  2880. d = dump_list(e->in_reply_to, d, off, 0);
  2881. d = dump_list(e->userhdrs, d, off, convert);
  2882. +#ifdef USE_NNTP
  2883. + d = dump_char(e->xref, d, off, 0);
  2884. + d = dump_char(e->followup_to, d, off, 0);
  2885. + d = dump_char(e->x_comment_to, d, off, convert);
  2886. +#endif
  2887. +
  2888. return d;
  2889. }
  2890. @@ -483,6 +489,12 @@ restore_envelope(ENVELOPE * e, const unsigned char *d, int *off, int convert)
  2891. restore_list(&e->references, d, off, 0);
  2892. restore_list(&e->in_reply_to, d, off, 0);
  2893. restore_list(&e->userhdrs, d, off, convert);
  2894. +
  2895. +#ifdef USE_NNTP
  2896. + restore_char(&e->xref, d, off, 0);
  2897. + restore_char(&e->followup_to, d, off, 0);
  2898. + restore_char(&e->x_comment_to, d, off, convert);
  2899. +#endif
  2900. }
  2901. static int
  2902. diff --git a/hdrline.c b/hdrline.c
  2903. index 21adc28..c05d28a 100644
  2904. --- a/hdrline.c
  2905. +++ b/hdrline.c
  2906. @@ -211,6 +211,7 @@ int mutt_user_is_recipient (HEADER *h)
  2907. * %E = number of messages in current thread
  2908. * %f = entire from line
  2909. * %F = like %n, unless from self
  2910. + * %g = newsgroup name (if compiled with NNTP support)
  2911. * %i = message-id
  2912. * %l = number of lines in the message
  2913. * %L = like %F, except `lists' are displayed first
  2914. @@ -219,12 +220,14 @@ int mutt_user_is_recipient (HEADER *h)
  2915. * %N = score
  2916. * %O = like %L, except using address instead of name
  2917. * %P = progress indicator for builtin pager
  2918. + * %R = `x-comment-to:' field (if present and compiled with NNTP support)
  2919. * %s = subject
  2920. * %S = short message status (e.g., N/O/D/!/r/-)
  2921. * %t = `to:' field (recipients)
  2922. * %T = $to_chars
  2923. * %u = user (login) name of author
  2924. * %v = first name of author, unless from self
  2925. + * %W = where user is (organization)
  2926. * %X = number of MIME attachments
  2927. * %y = `x-label:' field (if present)
  2928. * %Y = `x-label:' field (if present, tree unfolded, and != parent's x-label)
  2929. @@ -457,6 +460,12 @@ hdr_format_str (char *dest,
  2930. break;
  2931. +#ifdef USE_NNTP
  2932. + case 'g':
  2933. + mutt_format_s (dest, destlen, prefix, hdr->env->newsgroups ? hdr->env->newsgroups : "");
  2934. + break;
  2935. +#endif
  2936. +
  2937. case 'i':
  2938. mutt_format_s (dest, destlen, prefix, hdr->env->message_id ? hdr->env->message_id : "<no.id>");
  2939. break;
  2940. @@ -548,6 +557,15 @@ hdr_format_str (char *dest,
  2941. strfcpy(dest, NONULL(hfi->pager_progress), destlen);
  2942. break;
  2943. +#ifdef USE_NNTP
  2944. + case 'R':
  2945. + if (!optional)
  2946. + mutt_format_s (dest, destlen, prefix, hdr->env->x_comment_to ? hdr->env->x_comment_to : "");
  2947. + else if (!hdr->env->x_comment_to)
  2948. + optional = 0;
  2949. + break;
  2950. +#endif
  2951. +
  2952. case 's':
  2953. if (flags & M_FORMAT_TREE && !hdr->collapsed)
  2954. @@ -637,6 +655,13 @@ hdr_format_str (char *dest,
  2955. mutt_format_s (dest, destlen, prefix, buf2);
  2956. break;
  2957. + case 'W':
  2958. + if (!optional)
  2959. + mutt_format_s (dest, destlen, prefix, hdr->env->organization ? hdr->env->organization : "");
  2960. + else if (!hdr->env->organization)
  2961. + optional = 0;
  2962. + break;
  2963. +
  2964. case 'Z':
  2965. ch = ' ';
  2966. diff --git a/headers.c b/headers.c
  2967. index f701c8e..e817dad 100644
  2968. --- a/headers.c
  2969. +++ b/headers.c
  2970. @@ -114,6 +114,9 @@ void mutt_edit_headers (const char *editor,
  2971. $edit_headers set, we remove References: as they're likely invalid;
  2972. we can simply compare strings as we don't generate References for
  2973. multiple Message-Ids in IRT anyways */
  2974. +#ifdef USE_NNTP
  2975. + if (!option (OPTNEWSSEND))
  2976. +#endif
  2977. if (msg->env->in_reply_to &&
  2978. (!n->in_reply_to || mutt_strcmp (n->in_reply_to->data,
  2979. msg->env->in_reply_to->data) != 0))
  2980. diff --git a/init.c b/init.c
  2981. index 20a66bd..6ab1020 100644
  2982. --- a/init.c
  2983. +++ b/init.c
  2984. @@ -3104,6 +3104,28 @@ void mutt_init (int skip_sys_rc, LIST *commands)
  2985. else
  2986. Fqdn = safe_strdup(NONULL(Hostname));
  2987. +#ifdef USE_NNTP
  2988. + {
  2989. + FILE *f;
  2990. + char *i;
  2991. +
  2992. + if ((f = safe_fopen (SYSCONFDIR "/nntpserver", "r")))
  2993. + {
  2994. + buffer[0] = '\0';
  2995. + fgets (buffer, sizeof (buffer), f);
  2996. + p = &buffer;
  2997. + SKIPWS (p);
  2998. + i = p;
  2999. + while (*i && (*i != ' ') && (*i != '\t') && (*i != '\r') && (*i != '\n')) i++;
  3000. + *i = '\0';
  3001. + NewsServer = safe_strdup (p);
  3002. + fclose (f);
  3003. + }
  3004. + }
  3005. + if ((p = getenv ("NNTPSERVER")))
  3006. + NewsServer = safe_strdup (p);
  3007. +#endif
  3008. +
  3009. if ((p = getenv ("MAIL")))
  3010. Spoolfile = safe_strdup (p);
  3011. else if ((p = getenv ("MAILDIR")))
  3012. diff --git a/init.h b/init.h
  3013. index e20a24e..6efadb2 100644
  3014. --- a/init.h
  3015. +++ b/init.h
  3016. @@ -176,6 +176,20 @@ struct option_t MuttVars[] = {
  3017. ** If \fIset\fP, Mutt will prompt you for carbon-copy (Cc) recipients before
  3018. ** editing the body of an outgoing message.
  3019. */
  3020. +#ifdef USE_NNTP
  3021. + { "ask_follow_up", DT_BOOL, R_NONE, OPTASKFOLLOWUP, 0 },
  3022. + /*
  3023. + ** .pp
  3024. + ** If set, Mutt will prompt you for follow-up groups before editing
  3025. + ** the body of an outgoing message.
  3026. + */
  3027. + { "ask_x_comment_to", DT_BOOL, R_NONE, OPTASKXCOMMENTTO, 0 },
  3028. + /*
  3029. + ** .pp
  3030. + ** If set, Mutt will prompt you for x-comment-to field before editing
  3031. + ** the body of an outgoing message.
  3032. + */
  3033. +#endif
  3034. { "assumed_charset", DT_STR, R_NONE, UL &AssumedCharset, UL 0},
  3035. /*
  3036. ** .pp
  3037. @@ -328,6 +342,14 @@ struct option_t MuttVars[] = {
  3038. ** follow these menus. The option is \fIunset\fP by default because many
  3039. ** visual terminals don't permit making the cursor invisible.
  3040. */
  3041. +#ifdef USE_NNTP
  3042. + { "catchup_newsgroup", DT_QUAD, R_NONE, OPT_CATCHUP, M_ASKYES },
  3043. + /*
  3044. + ** .pp
  3045. + ** If this variable is \fIset\fP, Mutt will mark all articles in newsgroup
  3046. + ** as read when you quit the newsgroup (catchup newsgroup).
  3047. + */
  3048. +#endif
  3049. #if defined(USE_SSL)
  3050. { "certificate_file", DT_PATH, R_NONE, UL &SslCertFile, UL "~/.mutt_certificates" },
  3051. /*
  3052. @@ -815,6 +837,16 @@ struct option_t MuttVars[] = {
  3053. ** sent to both the list and your address, resulting in two copies
  3054. ** of the same email for you.
  3055. */
  3056. +#ifdef USE_NNTP
  3057. + { "followup_to_poster", DT_QUAD, R_NONE, OPT_FOLLOWUPTOPOSTER, M_ASKYES },
  3058. + /*
  3059. + ** .pp
  3060. + ** If this variable is \fIset\fP and the keyword "poster" is present in
  3061. + ** \fIFollowup-To\fP header, follow-up to newsgroup function is not
  3062. + ** permitted. The message will be mailed to the submitter of the
  3063. + ** message via mail.
  3064. + */
  3065. +#endif
  3066. { "force_name", DT_BOOL, R_NONE, OPTFORCENAME, 0 },
  3067. /*
  3068. ** .pp
  3069. @@ -897,6 +929,26 @@ struct option_t MuttVars[] = {
  3070. ** a regular expression that will match the whole name so mutt will expand
  3071. ** ``Franklin'' to ``Franklin, Steve''.
  3072. */
  3073. +#ifdef USE_NNTP
  3074. + { "group_index_format", DT_STR, R_BOTH, UL &GroupFormat, UL "%4C %M%N %5s %-45.45f %d" },
  3075. + /*
  3076. + ** .pp
  3077. + ** This variable allows you to customize the newsgroup browser display to
  3078. + ** your personal taste. This string is similar to ``$index_format'', but
  3079. + ** has its own set of printf()-like sequences:
  3080. + ** .dl
  3081. + ** .dt %C .dd current newsgroup number
  3082. + ** .dt %d .dd description of newsgroup (becomes from server)
  3083. + ** .dt %f .dd newsgroup name
  3084. + ** .dt %M .dd - if newsgroup not allowed for direct post (moderated for example)
  3085. + ** .dt %N .dd N if newsgroup is new, u if unsubscribed, blank otherwise
  3086. + ** .dt %n .dd number of new articles in newsgroup
  3087. + ** .dt %s .dd number of unread articles in newsgroup
  3088. + ** .dt %>X .dd right justify the rest of the string and pad with character "X"
  3089. + ** .dt %|X .dd pad to the end of the line with character "X"
  3090. + ** .de
  3091. + */
  3092. +#endif
  3093. { "hdr_format", DT_SYN, R_NONE, UL "index_format", 0 },
  3094. /*
  3095. */
  3096. @@ -1278,6 +1330,7 @@ struct option_t MuttVars[] = {
  3097. ** .dt %E .dd number of messages in current thread
  3098. ** .dt %f .dd sender (address + real name), either From: or Return-Path:
  3099. ** .dt %F .dd author name, or recipient name if the message is from you
  3100. + ** .dt %g .dd newsgroup name (if compiled with NNTP support)
  3101. ** .dt %H .dd spam attribute(s) of this message
  3102. ** .dt %i .dd message-id of the current message
  3103. ** .dt %l .dd number of lines in the message (does not work with maildir,
  3104. @@ -1293,12 +1346,14 @@ struct option_t MuttVars[] = {
  3105. ** stashed the message: list name or recipient name
  3106. ** if not sent to a list
  3107. ** .dt %P .dd progress indicator for the built-in pager (how much of the file has been displayed)
  3108. + ** .dt %R .dd ``X-Comment-To:'' field (if present and compiled with NNTP support)
  3109. ** .dt %s .dd subject of the message
  3110. ** .dt %S .dd status of the message (``N''/``D''/``d''/``!''/``r''/\(as)
  3111. ** .dt %t .dd ``To:'' field (recipients)
  3112. ** .dt %T .dd the appropriate character from the $$to_chars string
  3113. ** .dt %u .dd user (login) name of the author
  3114. ** .dt %v .dd first name of the author, or the recipient if the message is from you
  3115. + ** .dt %W .dd name of organization of author (``Organization:'' field)
  3116. ** .dt %X .dd number of attachments
  3117. ** (please see the ``$attachments'' section for possible speed effects)
  3118. ** .dt %y .dd ``X-Label:'' field, if present
  3119. @@ -1333,6 +1388,25 @@ struct option_t MuttVars[] = {
  3120. ** Note that these expandos are supported in
  3121. ** ``$save-hook'', ``$fcc-hook'' and ``$fcc-save-hook'', too.
  3122. */
  3123. +#ifdef USE_NNTP
  3124. + { "inews", DT_PATH, R_NONE, UL &Inews, UL "" },
  3125. + /*
  3126. + ** .pp
  3127. + ** If set, specifies the program and arguments used to deliver news posted
  3128. + ** by Mutt. Otherwise, mutt posts article using current connection to
  3129. + ** news server. The following printf-style sequence is understood:
  3130. + ** .dl
  3131. + ** .dt %a .dd account url
  3132. + ** .dt %p .dd port
  3133. + ** .dt %P .dd port if specified
  3134. + ** .dt %s .dd news server name
  3135. + ** .dt %S .dd url schema
  3136. + ** .dt %u .dd username
  3137. + ** .de
  3138. + ** .pp
  3139. + ** Example: set inews="/usr/local/bin/inews -hS"
  3140. + */
  3141. +#endif
  3142. { "ispell", DT_PATH, R_NONE, UL &Ispell, UL ISPELL },
  3143. /*
  3144. ** .pp
  3145. @@ -1567,6 +1641,15 @@ struct option_t MuttVars[] = {
  3146. ** menu, attachments which cannot be decoded in a reasonable manner will
  3147. ** be attached to the newly composed message if this option is \fIset\fP.
  3148. */
  3149. +#ifdef USE_NNTP
  3150. + { "mime_subject", DT_BOOL, R_NONE, OPTMIMESUBJECT, 1 },
  3151. + /*
  3152. + ** .pp
  3153. + ** If \fIunset\fP, 8-bit ``subject:'' line in article header will not be
  3154. + ** encoded according to RFC2047 to base64. This is useful when message
  3155. + ** is Usenet article, because MIME for news is nonstandard feature.
  3156. + */
  3157. +#endif
  3158. #ifdef MIXMASTER
  3159. { "mix_entry_format", DT_STR, R_NONE, UL &MixEntryFormat, UL "%4n %c %-16s %a" },
  3160. /*
  3161. @@ -1617,6 +1700,100 @@ struct option_t MuttVars[] = {
  3162. ** See also $$read_inc, $$write_inc and $$net_inc.
  3163. */
  3164. #endif
  3165. +#ifdef USE_NNTP
  3166. + { "news_cache_dir", DT_PATH, R_NONE, UL &NewsCacheDir, UL "~/.mutt" },
  3167. + /*
  3168. + ** .pp
  3169. + ** This variable pointing to directory where Mutt will save cached news
  3170. + ** articles and headers in. If \fIunset\fP, articles and headers will not be
  3171. + ** saved at all and will be reloaded from the server each time.
  3172. + */
  3173. + { "news_server", DT_STR, R_NONE, UL &NewsServer, 0 },
  3174. + /*
  3175. + ** .pp
  3176. + ** This variable specifies domain name or address of NNTP server. It
  3177. + ** defaults to the news server specified in the environment variable
  3178. + ** $$$NNTPSERVER or contained in the file /etc/nntpserver. You can also
  3179. + ** specify username and an alternative port for each news server, ie:
  3180. + ** .pp
  3181. + ** [[s]news://][username[:password]@]server[:port]
  3182. + */
  3183. + { "newsgroups_charset", DT_STR, R_NONE, UL &NewsgroupsCharset, UL "utf-8" },
  3184. + /*
  3185. + ** .pp
  3186. + ** Character set of newsgroups descriptions.
  3187. + */
  3188. + { "newsrc", DT_PATH, R_NONE, UL &NewsRc, UL "~/.newsrc" },
  3189. + /*
  3190. + ** .pp
  3191. + ** The file, containing info about subscribed newsgroups - names and
  3192. + ** indexes of read articles. The following printf-style sequence
  3193. + ** is understood:
  3194. + ** .dl
  3195. + ** .dt %a .dd account url
  3196. + ** .dt %p .dd port
  3197. + ** .dt %P .dd port if specified
  3198. + ** .dt %s .dd news server name
  3199. + ** .dt %S .dd url schema
  3200. + ** .dt %u .dd username
  3201. + ** .de
  3202. + */
  3203. + { "nntp_authenticators", DT_STR, R_NONE, UL &NntpAuthenticators, UL 0 },
  3204. + /*
  3205. + ** .pp
  3206. + ** This is a colon-delimited list of authentication methods mutt may
  3207. + ** attempt to use to log in to a news server, in the order mutt should
  3208. + ** try them. Authentication methods are either ``user'' or any
  3209. + ** SASL mechanism, e.g. ``digest-md5'', ``gssapi'' or ``cram-md5''.
  3210. + ** This option is case-insensitive. If it's \fIunset\fP (the default)
  3211. + ** mutt will try all available methods, in order from most-secure to
  3212. + ** least-secure.
  3213. + ** .pp
  3214. + ** Example:
  3215. + ** .ts
  3216. + ** set nntp_authenticators="digest-md5:user"
  3217. + ** .te
  3218. + ** .pp
  3219. + ** \fBNote:\fP Mutt will only fall back to other authentication methods if
  3220. + ** the previous methods are unavailable. If a method is available but
  3221. + ** authentication fails, mutt will not connect to the IMAP server.
  3222. + */
  3223. + { "nntp_context", DT_NUM, R_NONE, UL &NntpContext, 1000 },
  3224. + /*
  3225. + ** .pp
  3226. + ** This variable defines number of articles which will be in index when
  3227. + ** newsgroup entered. If active newsgroup have more articles than this
  3228. + ** number, oldest articles will be ignored. Also controls how many
  3229. + ** articles headers will be saved in cache when you quit newsgroup.
  3230. + */
  3231. + { "nntp_load_description", DT_BOOL, R_NONE, OPTLOADDESC, 1 },
  3232. + /*
  3233. + ** .pp
  3234. + ** This variable controls whether or not descriptions for each newsgroup
  3235. + ** must be loaded when newsgroup is added to list (first time list
  3236. + ** loading or new newsgroup adding).
  3237. + */
  3238. + { "nntp_user", DT_STR, R_NONE, UL &NntpUser, UL "" },
  3239. + /*
  3240. + ** .pp
  3241. + ** Your login name on the NNTP server. If \fIunset\fP and NNTP server requires
  3242. + ** authentification, Mutt will prompt you for your account name when you
  3243. + ** connect to news server.
  3244. + */
  3245. + { "nntp_pass", DT_STR, R_NONE, UL &NntpPass, UL "" },
  3246. + /*
  3247. + ** .pp
  3248. + ** Your password for NNTP account.
  3249. + */
  3250. + { "nntp_poll", DT_NUM, R_NONE, UL &NewsPollTimeout, 60 },
  3251. + /*
  3252. + ** .pp
  3253. + ** The time in seconds until any operations on newsgroup except post new
  3254. + ** article will cause recheck for new news. If set to 0, Mutt will
  3255. + ** recheck newsgroup on each operation in index (stepping, read article,
  3256. + ** etc.).
  3257. + */
  3258. +#endif
  3259. { "pager", DT_PATH, R_NONE, UL &Pager, UL "builtin" },
  3260. /*
  3261. ** .pp
  3262. @@ -2152,6 +2329,16 @@ struct option_t MuttVars[] = {
  3263. { "post_indent_str", DT_SYN, R_NONE, UL "post_indent_string", 0 },
  3264. /*
  3265. */
  3266. +#ifdef USE_NNTP
  3267. + { "post_moderated", DT_QUAD, R_NONE, OPT_TOMODERATED, M_ASKYES },
  3268. + /*
  3269. + ** .pp
  3270. + ** If set to \fIyes\fP, Mutt will post article to newsgroup that have
  3271. + ** not permissions to posting (e.g. moderated). \fBNote:\fP if news server
  3272. + ** does not support posting to that newsgroup or totally read-only, that
  3273. + ** posting will not have an effect.
  3274. + */
  3275. +#endif
  3276. { "postpone", DT_QUAD, R_NONE, OPT_POSTPONE, M_ASKYES },
  3277. /*
  3278. ** .pp
  3279. @@ -2576,6 +2763,28 @@ struct option_t MuttVars[] = {
  3280. ** Command to use when spawning a subshell. By default, the user's login
  3281. ** shell from \fC/etc/passwd\fP is used.
  3282. */
  3283. +#ifdef USE_NNTP
  3284. + { "save_unsubscribed", DT_BOOL, R_NONE, OPTSAVEUNSUB, 0 },
  3285. + /*
  3286. + ** .pp
  3287. + ** When \fIset\fP, info about unsubscribed newsgroups will be saved into
  3288. + ** ``newsrc'' file and into cache.
  3289. + */
  3290. + { "show_new_news", DT_BOOL, R_NONE, OPTSHOWNEWNEWS, 1 },
  3291. + /*
  3292. + ** .pp
  3293. + ** If \fIset\fP, news server will be asked for new newsgroups on entering
  3294. + ** the browser. Otherwise, it will be done only once for a news server.
  3295. + ** Also controls whether or not number of new articles of subscribed
  3296. + ** newsgroups will be then checked.
  3297. + */
  3298. + { "show_only_unread", DT_BOOL, R_NONE, OPTSHOWONLYUNREAD, 0 },
  3299. + /*
  3300. + ** .pp
  3301. + ** If \fIset\fP, only subscribed newsgroups that contain unread articles
  3302. + ** will be displayed in browser.
  3303. + */
  3304. +#endif
  3305. { "sig_dashes", DT_BOOL, R_NONE, OPTSIGDASHES, 1 },
  3306. /*
  3307. ** .pp
  3308. @@ -3511,6 +3720,14 @@ struct option_t MuttVars[] = {
  3309. ** xterm_set_titles has been set. This string is identical in formatting
  3310. ** to the one used by ``$$status_format''.
  3311. */
  3312. +#ifdef USE_NNTP
  3313. + { "x_comment_to", DT_BOOL, R_NONE, OPTXCOMMENTTO, 0 },
  3314. + /*
  3315. + ** .pp
  3316. + ** If \fIset\fP, Mutt will add ``X-Comment-To:'' field (that contains full
  3317. + ** name of original article author) to article that followuped to newsgroup.
  3318. + */
  3319. +#endif
  3320. /*--*/
  3321. { NULL, 0, 0, 0, 0 }
  3322. };
  3323. diff --git a/keymap.c b/keymap.c
  3324. index 9dc87f0..5cdef30 100644
  3325. --- a/keymap.c
  3326. +++ b/keymap.c
  3327. @@ -784,7 +784,6 @@ void km_init (void)
  3328. km_bindkey ("<enter>", MENU_MAIN, OP_DISPLAY_MESSAGE);
  3329. km_bindkey ("x", MENU_PAGER, OP_EXIT);
  3330. - km_bindkey ("i", MENU_PAGER, OP_EXIT);
  3331. km_bindkey ("<backspace>", MENU_PAGER, OP_PREV_LINE);
  3332. km_bindkey ("<pagedown>", MENU_PAGER, OP_NEXT_PAGE);
  3333. km_bindkey ("<pageup>", MENU_PAGER, OP_PREV_PAGE);
  3334. diff --git a/mailbox.h b/mailbox.h
  3335. index b652628..ed7954c 100644
  3336. --- a/mailbox.h
  3337. +++ b/mailbox.h
  3338. @@ -75,6 +75,9 @@ int mx_is_imap (const char *);
  3339. #ifdef USE_POP
  3340. int mx_is_pop (const char *);
  3341. #endif
  3342. +#ifdef USE_NNTP
  3343. +int mx_is_nntp (const char *);
  3344. +#endif
  3345. int mx_access (const char*, int);
  3346. int mx_check_empty (const char *);
  3347. diff --git a/main.c b/main.c
  3348. index 5ab1868..a291397 100644
  3349. --- a/main.c
  3350. +++ b/main.c
  3351. @@ -62,6 +62,10 @@
  3352. #include <idn/stringprep.h>
  3353. #endif
  3354. +#ifdef USE_NNTP
  3355. +#include "nntp.h"
  3356. +#endif
  3357. +
  3358. static const char *ReachingUs = N_("\
  3359. To contact the developers, please mail to <mutt-dev@mutt.org>.\n\
  3360. To report a bug, please visit http://bugs.mutt.org/.\n");
  3361. @@ -136,6 +140,8 @@ options:\n\
  3362. " -e <command>\tspecify a command to be executed after initialization\n\
  3363. -f <file>\tspecify which mailbox to read\n\
  3364. -F <file>\tspecify an alternate muttrc file\n\
  3365. + -g <server>\tspecify a news server (if compiled with NNTP)\n\
  3366. + -G\t\tselect a newsgroup (if compiled with NNTP)\n\
  3367. -H <file>\tspecify a draft file to read header and body from\n\
  3368. -i <file>\tspecify a file which Mutt should include in the body\n\
  3369. -m <type>\tspecify a default mailbox type\n\
  3370. @@ -284,6 +290,12 @@ static void show_version (void)
  3371. "-USE_POP "
  3372. #endif
  3373. +#ifdef USE_NNTP
  3374. + "+USE_NNTP "
  3375. +#else
  3376. + "-USE_NNTP "
  3377. +#endif
  3378. +
  3379. #ifdef USE_IMAP
  3380. "+USE_IMAP "
  3381. #else
  3382. @@ -558,6 +570,9 @@ init_extended_keys();
  3383. #define M_NOSYSRC (1<<2) /* -n */
  3384. #define M_RO (1<<3) /* -R */
  3385. #define M_SELECT (1<<4) /* -y */
  3386. +#ifdef USE_NNTP
  3387. +#define M_NEWS (1<<5) /* -g and -G */
  3388. +#endif
  3389. int main (int argc, char **argv)
  3390. {
  3391. @@ -630,7 +645,11 @@ int main (int argc, char **argv)
  3392. argv[nargc++] = argv[optind];
  3393. }
  3394. +#ifdef USE_NNTP
  3395. + if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:e:g:GH:s:i:hm:npQ:RvxyzZ")) != EOF)
  3396. +#else
  3397. if ((i = getopt (argc, argv, "+A:a:b:F:f:c:Dd:e:H:s:i:hm:npQ:RvxyzZ")) != EOF)
  3398. +#endif
  3399. switch (i)
  3400. {
  3401. case 'A':
  3402. @@ -727,6 +746,20 @@ int main (int argc, char **argv)
  3403. flags |= M_SELECT;
  3404. break;
  3405. +#ifdef USE_NNTP
  3406. + case 'g': /* Specify a news server */
  3407. + {
  3408. + char buf[LONG_STRING];
  3409. +
  3410. + snprintf (buf, sizeof (buf), "set news_server=%s", optarg);
  3411. + commands = mutt_add_list (commands, buf);
  3412. + }
  3413. +
  3414. + case 'G': /* List of newsgroups */
  3415. + flags |= M_SELECT | M_NEWS;
  3416. + break;
  3417. +#endif
  3418. +
  3419. case 'z':
  3420. flags |= M_IGNORE;
  3421. break;
  3422. @@ -1014,6 +1047,18 @@ int main (int argc, char **argv)
  3423. }
  3424. else if (flags & M_SELECT)
  3425. {
  3426. +#ifdef USE_NNTP
  3427. + if (flags & M_NEWS)
  3428. + {
  3429. + set_option (OPTNEWS);
  3430. + if(!(CurrentNewsSrv = nntp_select_server (NewsServer, 0)))
  3431. + {
  3432. + mutt_endwin (Errorbuf);
  3433. + exit (1);
  3434. + }
  3435. + }
  3436. + else
  3437. +#endif
  3438. if (!Incoming) {
  3439. mutt_endwin _("No incoming mailboxes defined.");
  3440. exit (1);
  3441. @@ -1029,6 +1074,15 @@ int main (int argc, char **argv)
  3442. if (!folder[0])
  3443. strfcpy (folder, NONULL(Spoolfile), sizeof (folder));
  3444. +
  3445. +#ifdef USE_NNTP
  3446. + if (option (OPTNEWS))
  3447. + {
  3448. + unset_option (OPTNEWS);
  3449. + nntp_expand_path (folder, sizeof (folder), &CurrentNewsSrv->conn->account);
  3450. + }
  3451. + else
  3452. +#endif
  3453. mutt_expand_path (folder, sizeof (folder));
  3454. mutt_str_replace (&CurrentFolder, folder);
  3455. diff --git a/mutt.h b/mutt.h
  3456. index 932ef10..a95b249 100644
  3457. --- a/mutt.h
  3458. +++ b/mutt.h
  3459. @@ -239,6 +239,9 @@ enum
  3460. M_PGP_KEY,
  3461. M_XLABEL,
  3462. M_MIMEATTACH,
  3463. +#ifdef USE_NNTP
  3464. + M_NEWSGROUPS,
  3465. +#endif
  3466. /* Options for Mailcap lookup */
  3467. M_EDIT,
  3468. @@ -295,6 +298,11 @@ enum
  3469. #endif
  3470. OPT_SUBJECT,
  3471. OPT_VERIFYSIG, /* verify PGP signatures */
  3472. +#ifdef USE_NNTP
  3473. + OPT_TOMODERATED,
  3474. + OPT_CATCHUP,
  3475. + OPT_FOLLOWUPTOPOSTER,
  3476. +#endif
  3477. /* THIS MUST BE THE LAST VALUE. */
  3478. OPT_MAX
  3479. @@ -311,6 +319,7 @@ enum
  3480. #define SENDKEY (1<<7)
  3481. #define SENDRESEND (1<<8)
  3482. #define SENDPOSTPONEDFCC (1<<9) /* used by mutt_get_postponed() to signal that the x-mutt-fcc header field was present */
  3483. +#define SENDNEWS (1<<10)
  3484. /* flags to _mutt_select_file() */
  3485. #define M_SEL_BUFFY (1<<0)
  3486. @@ -330,6 +339,8 @@ enum
  3487. OPTASCIICHARS,
  3488. OPTASKBCC,
  3489. OPTASKCC,
  3490. + OPTASKFOLLOWUP,
  3491. + OPTASKXCOMMENTTO,
  3492. OPTATTACHSPLIT,
  3493. OPTAUTOEDIT,
  3494. OPTAUTOTAG,
  3495. @@ -410,6 +421,9 @@ enum
  3496. OPTMETOO,
  3497. OPTMHPURGE,
  3498. OPTMIMEFORWDECODE,
  3499. +#ifdef USE_NNTP
  3500. + OPTMIMESUBJECT, /* encode subject line with RFC2047 */
  3501. +#endif
  3502. OPTNARROWTREE,
  3503. OPTPAGERSTOP,
  3504. OPTPIPEDECODE,
  3505. @@ -495,6 +509,16 @@ enum
  3506. OPTPGPAUTOINLINE,
  3507. OPTPGPREPLYINLINE,
  3508. + /* news options */
  3509. +
  3510. +#ifdef USE_NNTP
  3511. + OPTSHOWNEWNEWS,
  3512. + OPTSHOWONLYUNREAD,
  3513. + OPTSAVEUNSUB,
  3514. + OPTLOADDESC,
  3515. + OPTXCOMMENTTO,
  3516. +#endif
  3517. +
  3518. /* pseudo options */
  3519. OPTAUXSORT, /* (pseudo) using auxiliary sort function */
  3520. @@ -515,6 +539,7 @@ enum
  3521. OPTSORTSUBTHREADS, /* (pseudo) used when $sort_aux changes */
  3522. OPTNEEDRESCORE, /* (pseudo) set when the `score' command is used */
  3523. OPTATTACHMSG, /* (pseudo) used by attach-message */
  3524. + OPTHIDEREAD, /* (pseudo) whether or not hide read messages */
  3525. OPTKEEPQUIET, /* (pseudo) shut up the message and refresh
  3526. * functions while we are executing an
  3527. * external program.
  3528. @@ -527,6 +552,11 @@ enum
  3529. OPTSIDEBARNEWMAILONLY,
  3530. +#ifdef USE_NNTP
  3531. + OPTNEWS, /* (pseudo) used to change reader mode */
  3532. + OPTNEWSSEND, /* (pseudo) used to change behavior when posting */
  3533. +#endif
  3534. +
  3535. OPTMAX
  3536. };
  3537. @@ -606,6 +636,13 @@ typedef struct envelope
  3538. char *supersedes;
  3539. char *date;
  3540. char *x_label;
  3541. + char *organization;
  3542. +#ifdef USE_NNTP
  3543. + char *newsgroups;
  3544. + char *xref;
  3545. + char *followup_to;
  3546. + char *x_comment_to;
  3547. +#endif
  3548. BUFFER *spam;
  3549. LIST *references; /* message references (in reverse order) */
  3550. LIST *in_reply_to; /* in-reply-to header content */
  3551. @@ -792,7 +829,7 @@ typedef struct header
  3552. int refno; /* message number on server */
  3553. #endif
  3554. -#if defined USE_POP || defined USE_IMAP
  3555. +#if defined USE_POP || defined USE_IMAP || defined USE_NNTP
  3556. void *data; /* driver-specific data */
  3557. #endif
  3558. diff --git a/mutt_sasl.c b/mutt_sasl.c
  3559. index 896825e..21da012 100644
  3560. --- a/mutt_sasl.c
  3561. +++ b/mutt_sasl.c
  3562. @@ -188,6 +188,11 @@ int mutt_sasl_client_new (CONNECTION* conn, sasl_conn_t** saslconn)
  3563. case M_ACCT_TYPE_SMTP:
  3564. service = "smtp";
  3565. break;
  3566. +#ifdef USE_NNTP
  3567. + case M_ACCT_TYPE_NNTP:
  3568. + service = "nntp";
  3569. + break;
  3570. +#endif
  3571. default:
  3572. mutt_error (_("Unknown SASL profile"));
  3573. return -1;
  3574. diff --git a/muttlib.c b/muttlib.c
  3575. index 9086f07..49389fc 100644
  3576. --- a/muttlib.c
  3577. +++ b/muttlib.c
  3578. @@ -337,7 +337,7 @@ void mutt_free_header (HEADER **h)
  3579. #ifdef MIXMASTER
  3580. mutt_free_list (&(*h)->chain);
  3581. #endif
  3582. -#if defined USE_POP || defined USE_IMAP
  3583. +#if defined USE_POP || defined USE_IMAP || defined USE_NNTP
  3584. FREE (&(*h)->data);
  3585. #endif
  3586. FREE (h); /* __FREE_CHECKED__ */
  3587. @@ -725,6 +725,13 @@ void mutt_free_envelope (ENVELOPE **p)
  3588. FREE (&(*p)->supersedes);
  3589. FREE (&(*p)->date);
  3590. FREE (&(*p)->x_label);
  3591. + FREE (&(*p)->organization);
  3592. +#ifdef USE_NNTP
  3593. + FREE (&(*p)->newsgroups);
  3594. + FREE (&(*p)->xref);
  3595. + FREE (&(*p)->followup_to);
  3596. + FREE (&(*p)->x_comment_to);
  3597. +#endif
  3598. mutt_buffer_free (&(*p)->spam);
  3599. @@ -1573,6 +1580,14 @@ int mutt_save_confirm (const char *s, struct stat *st)
  3600. }
  3601. }
  3602. +#ifdef USE_NNTP
  3603. + if (magic == M_NNTP)
  3604. + {
  3605. + mutt_error _("Can't save message to news server.");
  3606. + return 0;
  3607. + }
  3608. +#endif
  3609. +
  3610. if (stat (s, st) != -1)
  3611. {
  3612. if (magic == -1)
  3613. diff --git a/mx.c b/mx.c
  3614. index cbee47d..5bbbf2a 100644
  3615. --- a/mx.c
  3616. +++ b/mx.c
  3617. @@ -347,6 +347,22 @@ int mx_is_pop (const char *p)
  3618. }
  3619. #endif
  3620. +#ifdef USE_NNTP
  3621. +int mx_is_nntp (const char *p)
  3622. +{
  3623. + url_scheme_t scheme;
  3624. +
  3625. + if (!p)
  3626. + return 0;
  3627. +
  3628. + scheme = url_check_scheme (p);
  3629. + if (scheme == U_NNTP || scheme == U_NNTPS)
  3630. + return 1;
  3631. +
  3632. + return 0;
  3633. +}
  3634. +#endif
  3635. +
  3636. int mx_get_magic (const char *path)
  3637. {
  3638. struct stat st;
  3639. @@ -364,6 +380,11 @@ int mx_get_magic (const char *path)
  3640. return M_POP;
  3641. #endif /* USE_POP */
  3642. +#ifdef USE_NNTP
  3643. + if (mx_is_nntp (path))
  3644. + return M_NNTP;
  3645. +#endif /* USE_NNTP */
  3646. +
  3647. if (stat (path, &st) == -1)
  3648. {
  3649. dprint (1, (debugfile, "mx_get_magic(): unable to stat %s: %s (errno %d).\n",
  3650. @@ -691,6 +712,12 @@ CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx)
  3651. break;
  3652. #endif /* USE_POP */
  3653. +#ifdef USE_NNTP
  3654. + case M_NNTP:
  3655. + rc = nntp_open_mailbox (ctx);
  3656. + break;
  3657. +#endif /* USE_NNTP */
  3658. +
  3659. default:
  3660. rc = -1;
  3661. break;
  3662. @@ -803,6 +830,12 @@ static int sync_mailbox (CONTEXT *ctx, int *index_hint)
  3663. rc = pop_sync_mailbox (ctx, index_hint);
  3664. break;
  3665. #endif /* USE_POP */
  3666. +
  3667. +#ifdef USE_NNTP
  3668. + case M_NNTP:
  3669. + rc = nntp_sync_mailbox (ctx);
  3670. + break;
  3671. +#endif /* USE_NNTP */
  3672. }
  3673. #if 0
  3674. @@ -905,6 +938,25 @@ int mx_close_mailbox (CONTEXT *ctx, int *index_hint)
  3675. return 0;
  3676. }
  3677. +#ifdef USE_NNTP
  3678. + if (ctx->unread && ctx->magic == M_NNTP)
  3679. + {
  3680. + NNTP_DATA *nntp_data = ctx->data;
  3681. +
  3682. + if (nntp_data && nntp_data->nserv && nntp_data->group)
  3683. + {
  3684. + int rc = query_quadoption (OPT_CATCHUP, _("Mark all articles read?"));
  3685. + if (rc < 0)
  3686. + {
  3687. + ctx->closing = 0;
  3688. + return -1;
  3689. + }
  3690. + else if (rc == M_YES)
  3691. + mutt_newsgroup_catchup (nntp_data->nserv, nntp_data->group);
  3692. + }
  3693. + }
  3694. +#endif
  3695. +
  3696. for (i = 0; i < ctx->msgcount; i++)
  3697. {
  3698. if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read
  3699. @@ -912,6 +964,12 @@ int mx_close_mailbox (CONTEXT *ctx, int *index_hint)
  3700. read_msgs++;
  3701. }
  3702. +#ifdef USE_NNTP
  3703. + /* don't need to move articles from newsgroup */
  3704. + if (ctx->magic == M_NNTP)
  3705. + read_msgs = 0;
  3706. +#endif
  3707. +
  3708. if (read_msgs && quadoption (OPT_MOVE) != M_NO)
  3709. {
  3710. char *p;
  3711. @@ -1464,6 +1522,11 @@ int mx_check_mailbox (CONTEXT *ctx, int *index_hint, int lock)
  3712. case M_POP:
  3713. return (pop_check_mailbox (ctx, index_hint));
  3714. #endif /* USE_POP */
  3715. +
  3716. +#ifdef USE_NNTP
  3717. + case M_NNTP:
  3718. + return (nntp_check_mailbox (ctx, 0));
  3719. +#endif /* USE_NNTP */
  3720. }
  3721. }
  3722. @@ -1524,6 +1587,15 @@ MESSAGE *mx_open_message (CONTEXT *ctx, int msgno)
  3723. }
  3724. #endif /* USE_POP */
  3725. +#ifdef USE_NNTP
  3726. + case M_NNTP:
  3727. + {
  3728. + if (nntp_fetch_message (msg, ctx, msgno) != 0)
  3729. + FREE (&msg);
  3730. + break;
  3731. + }
  3732. +#endif /* USE_NNTP */
  3733. +
  3734. default:
  3735. dprint (1, (debugfile, "mx_open_message(): function not implemented for mailbox type %d.\n", ctx->magic));
  3736. FREE (&msg);
  3737. @@ -1599,6 +1671,9 @@ int mx_close_message (MESSAGE **msg)
  3738. int r = 0;
  3739. if ((*msg)->magic == M_MH || (*msg)->magic == M_MAILDIR
  3740. +#ifdef USE_NNTP
  3741. + || (*msg)->magic == M_NNTP
  3742. +#endif
  3743. || (*msg)->magic == M_IMAP || (*msg)->magic == M_POP)
  3744. {
  3745. r = safe_fclose (&(*msg)->fp);
  3746. diff --git a/mx.h b/mx.h
  3747. index 4aabadf..9980c4e 100644
  3748. --- a/mx.h
  3749. +++ b/mx.h
  3750. @@ -34,6 +34,9 @@ enum
  3751. M_MMDF,
  3752. M_MH,
  3753. M_MAILDIR,
  3754. +#ifdef USE_NNTP
  3755. + M_NNTP,
  3756. +#endif
  3757. M_IMAP,
  3758. M_POP
  3759. #ifdef USE_COMPRESSED
  3760. diff --git a/newsrc.c b/newsrc.c
  3761. new file mode 100644
  3762. index 0000000..482214c
  3763. --- /dev/null
  3764. +++ b/newsrc.c
  3765. @@ -0,0 +1,1260 @@
  3766. +/*
  3767. + * Copyright (C) 1998 Brandon Long <blong@fiction.net>
  3768. + * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
  3769. + * Copyright (C) 2000-2012 Vsevolod Volkov <vvv@mutt.org.ua>
  3770. + *
  3771. + * This program is free software; you can redistribute it and/or modify
  3772. + * it under the terms of the GNU General Public License as published by
  3773. + * the Free Software Foundation; either version 2 of the License, or
  3774. + * (at your option) any later version.
  3775. + *
  3776. + * This program is distributed in the hope that it will be useful,
  3777. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  3778. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  3779. + * GNU General Public License for more details.
  3780. + *
  3781. + * You should have received a copy of the GNU General Public License
  3782. + * along with this program; if not, write to the Free Software
  3783. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  3784. + */
  3785. +
  3786. +#if HAVE_CONFIG_H
  3787. +#include "config.h"
  3788. +#endif
  3789. +
  3790. +#include "mutt.h"
  3791. +#include "mutt_curses.h"
  3792. +#include "sort.h"
  3793. +#include "mx.h"
  3794. +#include "mime.h"
  3795. +#include "mailbox.h"
  3796. +#include "nntp.h"
  3797. +#include "rfc822.h"
  3798. +#include "rfc1524.h"
  3799. +#include "rfc2047.h"
  3800. +#include "bcache.h"
  3801. +
  3802. +#if USE_HCACHE
  3803. +#include "hcache.h"
  3804. +#endif
  3805. +
  3806. +#include <unistd.h>
  3807. +#include <string.h>
  3808. +#include <ctype.h>
  3809. +#include <stdlib.h>
  3810. +#include <sys/stat.h>
  3811. +#include <sys/types.h>
  3812. +#include <dirent.h>
  3813. +#include <errno.h>
  3814. +
  3815. +/* Find NNTP_DATA for given newsgroup or add it */
  3816. +static NNTP_DATA *nntp_data_find (NNTP_SERVER *nserv, const char *group)
  3817. +{
  3818. + NNTP_DATA *nntp_data = hash_find (nserv->groups_hash, group);
  3819. +
  3820. + if (!nntp_data)
  3821. + {
  3822. + /* create NNTP_DATA structure and add it to hash */
  3823. + nntp_data = safe_calloc (1, sizeof (NNTP_DATA) + strlen (group) + 1);
  3824. + nntp_data->group = (char *)nntp_data + sizeof (NNTP_DATA);
  3825. + strcpy (nntp_data->group, group);
  3826. + nntp_data->nserv = nserv;
  3827. + nntp_data->deleted = 1;
  3828. + if (nserv->groups_hash->nelem < nserv->groups_hash->curnelem * 2)
  3829. + nserv->groups_hash = hash_resize (nserv->groups_hash,
  3830. + nserv->groups_hash->nelem * 2, 0);
  3831. + hash_insert (nserv->groups_hash, nntp_data->group, nntp_data, 0);
  3832. +
  3833. + /* add NNTP_DATA to list */
  3834. + if (nserv->groups_num >= nserv->groups_max)
  3835. + {
  3836. + nserv->groups_max *= 2;
  3837. + safe_realloc (&nserv->groups_list,
  3838. + nserv->groups_max * sizeof (nntp_data));
  3839. + }
  3840. + nserv->groups_list[nserv->groups_num++] = nntp_data;
  3841. + }
  3842. + return nntp_data;
  3843. +}
  3844. +
  3845. +/* Remove all temporarily cache files */
  3846. +void nntp_acache_free (NNTP_DATA *nntp_data)
  3847. +{
  3848. + int i;
  3849. +
  3850. + for (i = 0; i < NNTP_ACACHE_LEN; i++)
  3851. + {
  3852. + if (nntp_data->acache[i].path)
  3853. + {
  3854. + unlink (nntp_data->acache[i].path);
  3855. + FREE (&nntp_data->acache[i].path);
  3856. + }
  3857. + }
  3858. +}
  3859. +
  3860. +/* Free NNTP_DATA, used to destroy hash elements */
  3861. +void nntp_data_free (void *data)
  3862. +{
  3863. + NNTP_DATA *nntp_data = data;
  3864. +
  3865. + if (!nntp_data)
  3866. + return;
  3867. + nntp_acache_free (nntp_data);
  3868. + mutt_bcache_close (&nntp_data->bcache);
  3869. + FREE (&nntp_data->newsrc_ent);
  3870. + FREE (&nntp_data->desc);
  3871. + FREE (&data);
  3872. +}
  3873. +
  3874. +/* Unlock and close .newsrc file */
  3875. +void nntp_newsrc_close (NNTP_SERVER *nserv)
  3876. +{
  3877. + if (!nserv->newsrc_fp)
  3878. + return;
  3879. +
  3880. + dprint (1, (debugfile, "Unlocking %s\n", nserv->newsrc_file));
  3881. + mx_unlock_file (nserv->newsrc_file, fileno (nserv->newsrc_fp), 0);
  3882. + safe_fclose (&nserv->newsrc_fp);
  3883. +}
  3884. +
  3885. +/* Parse .newsrc file:
  3886. + * 0 - not changed
  3887. + * 1 - parsed
  3888. + * -1 - error */
  3889. +int nntp_newsrc_parse (NNTP_SERVER *nserv)
  3890. +{
  3891. + unsigned int i;
  3892. + char *line;
  3893. + struct stat sb;
  3894. +
  3895. + /* if file doesn't exist, create it */
  3896. + nserv->newsrc_fp = safe_fopen (nserv->newsrc_file, "a");
  3897. + safe_fclose (&nserv->newsrc_fp);
  3898. +
  3899. + /* open .newsrc */
  3900. + nserv->newsrc_fp = safe_fopen (nserv->newsrc_file, "r");
  3901. + if (!nserv->newsrc_fp)
  3902. + {
  3903. + mutt_perror (nserv->newsrc_file);
  3904. + mutt_sleep (2);
  3905. + return -1;
  3906. + }
  3907. +
  3908. + /* lock it */
  3909. + dprint (1, (debugfile, "Locking %s\n", nserv->newsrc_file));
  3910. + if (mx_lock_file (nserv->newsrc_file, fileno (nserv->newsrc_fp), 0, 0, 1))
  3911. + {
  3912. + safe_fclose (&nserv->newsrc_fp);
  3913. + return -1;
  3914. + }
  3915. +
  3916. + if (stat (nserv->newsrc_file, &sb))
  3917. + {
  3918. + mutt_perror (nserv->newsrc_file);
  3919. + nntp_newsrc_close (nserv);
  3920. + mutt_sleep (2);
  3921. + return -1;
  3922. + }
  3923. +
  3924. + if (nserv->size == sb.st_size && nserv->mtime == sb.st_mtime)
  3925. + return 0;
  3926. +
  3927. + nserv->size = sb.st_size;
  3928. + nserv->mtime = sb.st_mtime;
  3929. + nserv->newsrc_modified = 1;
  3930. + dprint (1, (debugfile, "Parsing %s\n", nserv->newsrc_file));
  3931. +
  3932. + /* .newsrc has been externally modified or hasn't been loaded yet */
  3933. + for (i = 0; i < nserv->groups_num; i++)
  3934. + {
  3935. + NNTP_DATA *nntp_data = nserv->groups_list[i];
  3936. +
  3937. + if (!nntp_data)
  3938. + continue;
  3939. +
  3940. + nntp_data->subscribed = 0;
  3941. + nntp_data->newsrc_len = 0;
  3942. + FREE (&nntp_data->newsrc_ent);
  3943. + }
  3944. +
  3945. + line = safe_malloc (sb.st_size + 1);
  3946. + while (sb.st_size && fgets (line, sb.st_size + 1, nserv->newsrc_fp))
  3947. + {
  3948. + char *b, *h, *p;
  3949. + unsigned int subs = 0, i = 1;
  3950. + NNTP_DATA *nntp_data;
  3951. +
  3952. + /* find end of newsgroup name */
  3953. + p = strpbrk (line, ":!");
  3954. + if (!p)
  3955. + continue;
  3956. +
  3957. + /* ":" - subscribed, "!" - unsubscribed */
  3958. + if (*p == ':')
  3959. + subs++;
  3960. + *p++ = '\0';
  3961. +
  3962. + /* get newsgroup data */
  3963. + nntp_data = nntp_data_find (nserv, line);
  3964. + FREE (&nntp_data->newsrc_ent);
  3965. +
  3966. + /* count number of entries */
  3967. + b = p;
  3968. + while (*b)
  3969. + if (*b++ == ',')
  3970. + i++;
  3971. + nntp_data->newsrc_ent = safe_calloc (i, sizeof (NEWSRC_ENTRY));
  3972. + nntp_data->subscribed = subs;
  3973. +
  3974. + /* parse entries */
  3975. + i = 0;
  3976. + while (p)
  3977. + {
  3978. + b = p;
  3979. +
  3980. + /* find end of entry */
  3981. + p = strchr (p, ',');
  3982. + if (p)
  3983. + *p++ = '\0';
  3984. +
  3985. + /* first-last or single number */
  3986. + h = strchr (b, '-');
  3987. + if (h)
  3988. + *h++ = '\0';
  3989. + else
  3990. + h = b;
  3991. +
  3992. + if (sscanf (b, ANUM, &nntp_data->newsrc_ent[i].first) == 1 &&
  3993. + sscanf (h, ANUM, &nntp_data->newsrc_ent[i].last) == 1)
  3994. + i++;
  3995. + }
  3996. + if (i == 0)
  3997. + {
  3998. + nntp_data->newsrc_ent[i].first = 1;
  3999. + nntp_data->newsrc_ent[i].last = 0;
  4000. + i++;
  4001. + }
  4002. + if (nntp_data->lastMessage == 0)
  4003. + nntp_data->lastMessage = nntp_data->newsrc_ent[i - 1].last;
  4004. + nntp_data->newsrc_len = i;
  4005. + safe_realloc (&nntp_data->newsrc_ent, i * sizeof (NEWSRC_ENTRY));
  4006. + nntp_group_unread_stat (nntp_data);
  4007. + dprint (2, (debugfile, "nntp_newsrc_parse: %s\n", nntp_data->group));
  4008. + }
  4009. + FREE (&line);
  4010. + return 1;
  4011. +}
  4012. +
  4013. +/* Generate array of .newsrc entries */
  4014. +void nntp_newsrc_gen_entries (CONTEXT *ctx)
  4015. +{
  4016. + NNTP_DATA *nntp_data = ctx->data;
  4017. + anum_t last = 0, first = 1;
  4018. + int series, i;
  4019. + int save_sort = SORT_ORDER;
  4020. + unsigned int entries;
  4021. +
  4022. + if (Sort != SORT_ORDER)
  4023. + {
  4024. + save_sort = Sort;
  4025. + Sort = SORT_ORDER;
  4026. + mutt_sort_headers (ctx, 0);
  4027. + }
  4028. +
  4029. + entries = nntp_data->newsrc_len;
  4030. + if (!entries)
  4031. + {
  4032. + entries = 5;
  4033. + nntp_data->newsrc_ent = safe_calloc (entries, sizeof (NEWSRC_ENTRY));
  4034. + }
  4035. +
  4036. + /* Set up to fake initial sequence from 1 to the article before the
  4037. + * first article in our list */
  4038. + nntp_data->newsrc_len = 0;
  4039. + series = 1;
  4040. + for (i = 0; i < ctx->msgcount; i++)
  4041. + {
  4042. + /* search for first unread */
  4043. + if (series)
  4044. + {
  4045. + /* We don't actually check sequential order, since we mark
  4046. + * "missing" entries as read/deleted */
  4047. + last = NHDR (ctx->hdrs[i])->article_num;
  4048. + if (last >= nntp_data->firstMessage && !ctx->hdrs[i]->deleted &&
  4049. + !ctx->hdrs[i]->read)
  4050. + {
  4051. + if (nntp_data->newsrc_len >= entries)
  4052. + {
  4053. + entries *= 2;
  4054. + safe_realloc (&nntp_data->newsrc_ent, entries * sizeof (NEWSRC_ENTRY));
  4055. + }
  4056. + nntp_data->newsrc_ent[nntp_data->newsrc_len].first = first;
  4057. + nntp_data->newsrc_ent[nntp_data->newsrc_len].last = last - 1;
  4058. + nntp_data->newsrc_len++;
  4059. + series = 0;
  4060. + }
  4061. + }
  4062. +
  4063. + /* search for first read */
  4064. + else
  4065. + {
  4066. + if (ctx->hdrs[i]->deleted || ctx->hdrs[i]->read)
  4067. + {
  4068. + first = last + 1;
  4069. + series = 1;
  4070. + }
  4071. + last = NHDR (ctx->hdrs[i])->article_num;
  4072. + }
  4073. + }
  4074. +
  4075. + if (series && first <= nntp_data->lastLoaded)
  4076. + {
  4077. + if (nntp_data->newsrc_len >= entries)
  4078. + {
  4079. + entries++;
  4080. + safe_realloc (&nntp_data->newsrc_ent, entries * sizeof (NEWSRC_ENTRY));
  4081. + }
  4082. + nntp_data->newsrc_ent[nntp_data->newsrc_len].first = first;
  4083. + nntp_data->newsrc_ent[nntp_data->newsrc_len].last = nntp_data->lastLoaded;
  4084. + nntp_data->newsrc_len++;
  4085. + }
  4086. + safe_realloc (&nntp_data->newsrc_ent,
  4087. + nntp_data->newsrc_len * sizeof (NEWSRC_ENTRY));
  4088. +
  4089. + if (save_sort != Sort)
  4090. + {
  4091. + Sort = save_sort;
  4092. + mutt_sort_headers (ctx, 0);
  4093. + }
  4094. +}
  4095. +
  4096. +/* Update file with new contents */
  4097. +static int update_file (char *filename, char *buf)
  4098. +{
  4099. + FILE *fp;
  4100. + char tmpfile[_POSIX_PATH_MAX];
  4101. + int rc = -1;
  4102. +
  4103. + while (1)
  4104. + {
  4105. + snprintf (tmpfile, sizeof (tmpfile), "%s.tmp", filename);
  4106. + fp = fopen (tmpfile, "w");
  4107. + if (!fp)
  4108. + {
  4109. + mutt_perror (tmpfile);
  4110. + *tmpfile = '\0';
  4111. + break;
  4112. + }
  4113. + if (fputs (buf, fp) == EOF)
  4114. + {
  4115. + mutt_perror (tmpfile);
  4116. + break;
  4117. + }
  4118. + if (fclose (fp) == EOF)
  4119. + {
  4120. + mutt_perror (tmpfile);
  4121. + fp = NULL;
  4122. + break;
  4123. + }
  4124. + fp = NULL;
  4125. + if (rename (tmpfile, filename) < 0)
  4126. + {
  4127. + mutt_perror (filename);
  4128. + break;
  4129. + }
  4130. + *tmpfile = '\0';
  4131. + rc = 0;
  4132. + break;
  4133. + }
  4134. + if (fp)
  4135. + fclose (fp);
  4136. + if (*tmpfile)
  4137. + unlink (tmpfile);
  4138. + if (rc)
  4139. + mutt_sleep (2);
  4140. + return rc;
  4141. +}
  4142. +
  4143. +/* Update .newsrc file */
  4144. +int nntp_newsrc_update (NNTP_SERVER *nserv)
  4145. +{
  4146. + char *buf;
  4147. + size_t buflen, off;
  4148. + unsigned int i;
  4149. + int rc = -1;
  4150. +
  4151. + if (!nserv)
  4152. + return -1;
  4153. +
  4154. + buflen = 10 * LONG_STRING;
  4155. + buf = safe_calloc (1, buflen);
  4156. + off = 0;
  4157. +
  4158. + /* we will generate full newsrc here */
  4159. + for (i = 0; i < nserv->groups_num; i++)
  4160. + {
  4161. + NNTP_DATA *nntp_data = nserv->groups_list[i];
  4162. + unsigned int n;
  4163. +
  4164. + if (!nntp_data || !nntp_data->newsrc_ent)
  4165. + continue;
  4166. +
  4167. + /* write newsgroup name */
  4168. + if (off + strlen (nntp_data->group) + 3 > buflen)
  4169. + {
  4170. + buflen *= 2;
  4171. + safe_realloc (&buf, buflen);
  4172. + }
  4173. + snprintf (buf + off, buflen - off, "%s%c ", nntp_data->group,
  4174. + nntp_data->subscribed ? ':' : '!');
  4175. + off += strlen (buf + off);
  4176. +
  4177. + /* write entries */
  4178. + for (n = 0; n < nntp_data->newsrc_len; n++)
  4179. + {
  4180. + if (off + LONG_STRING > buflen)
  4181. + {
  4182. + buflen *= 2;
  4183. + safe_realloc (&buf, buflen);
  4184. + }
  4185. + if (n)
  4186. + buf[off++] = ',';
  4187. + if (nntp_data->newsrc_ent[n].first == nntp_data->newsrc_ent[n].last)
  4188. + snprintf (buf + off, buflen - off, "%d", nntp_data->newsrc_ent[n].first);
  4189. + else if (nntp_data->newsrc_ent[n].first < nntp_data->newsrc_ent[n].last)
  4190. + snprintf (buf + off, buflen - off, "%d-%d",
  4191. + nntp_data->newsrc_ent[n].first, nntp_data->newsrc_ent[n].last);
  4192. + off += strlen (buf + off);
  4193. + }
  4194. + buf[off++] = '\n';
  4195. + }
  4196. + buf[off] = '\0';
  4197. +
  4198. + /* newrc being fully rewritten */
  4199. + dprint (1, (debugfile, "Updating %s\n", nserv->newsrc_file));
  4200. + if (nserv->newsrc_file && update_file (nserv->newsrc_file, buf) == 0)
  4201. + {
  4202. + struct stat sb;
  4203. +
  4204. + rc = stat (nserv->newsrc_file, &sb);
  4205. + if (rc == 0)
  4206. + {
  4207. + nserv->size = sb.st_size;
  4208. + nserv->mtime = sb.st_mtime;
  4209. + }
  4210. + else
  4211. + {
  4212. + mutt_perror (nserv->newsrc_file);
  4213. + mutt_sleep (2);
  4214. + }
  4215. + }
  4216. + FREE (&buf);
  4217. + return rc;
  4218. +}
  4219. +
  4220. +/* Make fully qualified cache file name */
  4221. +static void cache_expand (char *dst, size_t dstlen, ACCOUNT *acct, char *src)
  4222. +{
  4223. + char *c;
  4224. + char file[_POSIX_PATH_MAX];
  4225. +
  4226. + /* server subdirectory */
  4227. + if (acct)
  4228. + {
  4229. + ciss_url_t url;
  4230. +
  4231. + mutt_account_tourl (acct, &url);
  4232. + url.path = src;
  4233. + url_ciss_tostring (&url, file, sizeof (file), U_PATH);
  4234. + }
  4235. + else
  4236. + strfcpy (file, src ? src : "", sizeof (file));
  4237. +
  4238. + snprintf (dst, dstlen, "%s/%s", NewsCacheDir, file);
  4239. +
  4240. + /* remove trailing slash */
  4241. + c = dst + strlen (dst) - 1;
  4242. + if (*c == '/')
  4243. + *c = '\0';
  4244. + mutt_expand_path (dst, dstlen);
  4245. +}
  4246. +
  4247. +/* Make fully qualified url from newsgroup name */
  4248. +void nntp_expand_path (char *line, size_t len, ACCOUNT *acct)
  4249. +{
  4250. + ciss_url_t url;
  4251. +
  4252. + url.path = safe_strdup (line);
  4253. + mutt_account_tourl (acct, &url);
  4254. + url_ciss_tostring (&url, line, len, 0);
  4255. + FREE (&url.path);
  4256. +}
  4257. +
  4258. +/* Parse newsgroup */
  4259. +int nntp_add_group (char *line, void *data)
  4260. +{
  4261. + NNTP_SERVER *nserv = data;
  4262. + NNTP_DATA *nntp_data;
  4263. + char group[LONG_STRING];
  4264. + char desc[HUGE_STRING] = "";
  4265. + char mod;
  4266. + anum_t first, last;
  4267. +
  4268. + if (!nserv || !line)
  4269. + return 0;
  4270. +
  4271. + if (sscanf (line, "%s " ANUM " " ANUM " %c %[^\n]", group,
  4272. + &last, &first, &mod, desc) < 4)
  4273. + return 0;
  4274. +
  4275. + nntp_data = nntp_data_find (nserv, group);
  4276. + nntp_data->deleted = 0;
  4277. + nntp_data->firstMessage = first;
  4278. + nntp_data->lastMessage = last;
  4279. + nntp_data->allowed = mod == 'y' || mod == 'm' ? 1 : 0;
  4280. + mutt_str_replace (&nntp_data->desc, desc);
  4281. + if (nntp_data->newsrc_ent || nntp_data->lastCached)
  4282. + nntp_group_unread_stat (nntp_data);
  4283. + else if (nntp_data->lastMessage &&
  4284. + nntp_data->firstMessage <= nntp_data->lastMessage)
  4285. + nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1;
  4286. + else
  4287. + nntp_data->unread = 0;
  4288. + return 0;
  4289. +}
  4290. +
  4291. +/* Load list of all newsgroups from cache */
  4292. +static int active_get_cache (NNTP_SERVER *nserv)
  4293. +{
  4294. + char buf[HUGE_STRING];
  4295. + char file[_POSIX_PATH_MAX];
  4296. + time_t t;
  4297. + FILE *fp;
  4298. +
  4299. + cache_expand (file, sizeof (file), &nserv->conn->account, ".active");
  4300. + dprint (1, (debugfile, "Parsing %s\n", file));
  4301. + fp = safe_fopen (file, "r");
  4302. + if (!fp)
  4303. + return -1;
  4304. +
  4305. + if (fgets (buf, sizeof (buf), fp) == NULL ||
  4306. + sscanf (buf, "%ld%s", &t, file) != 1 || t == 0)
  4307. + {
  4308. + fclose (fp);
  4309. + return -1;
  4310. + }
  4311. + nserv->newgroups_time = t;
  4312. +
  4313. + mutt_message _("Loading list of groups from cache...");
  4314. + while (fgets (buf, sizeof (buf), fp))
  4315. + nntp_add_group (buf, nserv);
  4316. + nntp_add_group (NULL, NULL);
  4317. + fclose (fp);
  4318. + mutt_clear_error ();
  4319. + return 0;
  4320. +}
  4321. +
  4322. +/* Save list of all newsgroups to cache */
  4323. +int nntp_active_save_cache (NNTP_SERVER *nserv)
  4324. +{
  4325. + char file[_POSIX_PATH_MAX];
  4326. + char *buf;
  4327. + size_t buflen, off;
  4328. + unsigned int i;
  4329. + int rc;
  4330. +
  4331. + if (!nserv->cacheable)
  4332. + return 0;
  4333. +
  4334. + buflen = 10 * LONG_STRING;
  4335. + buf = safe_calloc (1, buflen);
  4336. + snprintf (buf, buflen, "%lu\n", (unsigned long)nserv->newgroups_time);
  4337. + off = strlen (buf);
  4338. +
  4339. + for (i = 0; i < nserv->groups_num; i++)
  4340. + {
  4341. + NNTP_DATA *nntp_data = nserv->groups_list[i];
  4342. +
  4343. + if (!nntp_data || nntp_data->deleted)
  4344. + continue;
  4345. +
  4346. + if (off + strlen (nntp_data->group) +
  4347. + (nntp_data->desc ? strlen (nntp_data->desc) : 0) + 50 > buflen)
  4348. + {
  4349. + buflen *= 2;
  4350. + safe_realloc (&buf, buflen);
  4351. + }
  4352. + snprintf (buf + off, buflen - off, "%s %d %d %c%s%s\n", nntp_data->group,
  4353. + nntp_data->lastMessage, nntp_data->firstMessage,
  4354. + nntp_data->allowed ? 'y' : 'n', nntp_data->desc ? " " : "",
  4355. + nntp_data->desc ? nntp_data->desc : "");
  4356. + off += strlen (buf + off);
  4357. + }
  4358. +
  4359. + cache_expand (file, sizeof (file), &nserv->conn->account, ".active");
  4360. + dprint (1, (debugfile, "Updating %s\n", file));
  4361. + rc = update_file (file, buf);
  4362. + FREE (&buf);
  4363. + return rc;
  4364. +}
  4365. +
  4366. +#ifdef USE_HCACHE
  4367. +/* Used by mutt_hcache_open() to compose hcache file name */
  4368. +static int nntp_hcache_namer (const char *path, char *dest, size_t destlen)
  4369. +{
  4370. + return snprintf (dest, destlen, "%s.hcache", path);
  4371. +}
  4372. +
  4373. +/* Open newsgroup hcache */
  4374. +header_cache_t *nntp_hcache_open (NNTP_DATA *nntp_data)
  4375. +{
  4376. + ciss_url_t url;
  4377. + char file[_POSIX_PATH_MAX];
  4378. +
  4379. + if (!nntp_data->nserv || !nntp_data->nserv->cacheable ||
  4380. + !nntp_data->nserv->conn || !nntp_data->group ||
  4381. + !(nntp_data->newsrc_ent || nntp_data->subscribed ||
  4382. + option (OPTSAVEUNSUB)))
  4383. + return NULL;
  4384. +
  4385. + mutt_account_tourl (&nntp_data->nserv->conn->account, &url);
  4386. + url.path = nntp_data->group;
  4387. + url_ciss_tostring (&url, file, sizeof (file), U_PATH);
  4388. + return mutt_hcache_open (NewsCacheDir, file, nntp_hcache_namer);
  4389. +}
  4390. +
  4391. +/* Remove stale cached headers */
  4392. +void nntp_hcache_update (NNTP_DATA *nntp_data, header_cache_t *hc)
  4393. +{
  4394. + char buf[16];
  4395. + int old = 0;
  4396. + void *hdata;
  4397. + anum_t first, last, current;
  4398. +
  4399. + if (!hc)
  4400. + return;
  4401. +
  4402. + /* fetch previous values of first and last */
  4403. + hdata = mutt_hcache_fetch_raw (hc, "index", strlen);
  4404. + if (hdata)
  4405. + {
  4406. + dprint (2, (debugfile,
  4407. + "nntp_hcache_update: mutt_hcache_fetch index: %s\n", hdata));
  4408. + if (sscanf (hdata, ANUM " " ANUM, &first, &last) == 2)
  4409. + {
  4410. + old = 1;
  4411. + nntp_data->lastCached = last;
  4412. +
  4413. + /* clean removed headers from cache */
  4414. + for (current = first; current <= last; current++)
  4415. + {
  4416. + if (current >= nntp_data->firstMessage &&
  4417. + current <= nntp_data->lastMessage)
  4418. + continue;
  4419. +
  4420. + snprintf (buf, sizeof (buf), "%d", current);
  4421. + dprint (2, (debugfile,
  4422. + "nntp_hcache_update: mutt_hcache_delete %s\n", buf));
  4423. + mutt_hcache_delete (hc, buf, strlen);
  4424. + }
  4425. + }
  4426. + }
  4427. +
  4428. + /* store current values of first and last */
  4429. + if (!old || nntp_data->firstMessage != first ||
  4430. + nntp_data->lastMessage != last)
  4431. + {
  4432. + snprintf (buf, sizeof (buf), "%u %u", nntp_data->firstMessage,
  4433. + nntp_data->lastMessage);
  4434. + dprint (2, (debugfile,
  4435. + "nntp_hcache_update: mutt_hcache_store index: %s\n", buf));
  4436. + mutt_hcache_store_raw (hc, "index", buf, strlen (buf) + 1, strlen);
  4437. + }
  4438. +}
  4439. +#endif
  4440. +
  4441. +/* Remove bcache file */
  4442. +static int nntp_bcache_delete (const char *id, body_cache_t *bcache, void *data)
  4443. +{
  4444. + NNTP_DATA *nntp_data = data;
  4445. + anum_t anum;
  4446. + char c;
  4447. +
  4448. + if (!nntp_data || sscanf (id, ANUM "%c", &anum, &c) != 1 ||
  4449. + anum < nntp_data->firstMessage || anum > nntp_data->lastMessage)
  4450. + {
  4451. + if (nntp_data)
  4452. + dprint (2, (debugfile, "nntp_bcache_delete: mutt_bcache_del %s\n", id));
  4453. + mutt_bcache_del (bcache, id);
  4454. + }
  4455. + return 0;
  4456. +}
  4457. +
  4458. +/* Remove stale cached messages */
  4459. +void nntp_bcache_update (NNTP_DATA *nntp_data)
  4460. +{
  4461. + mutt_bcache_list (nntp_data->bcache, nntp_bcache_delete, nntp_data);
  4462. +}
  4463. +
  4464. +/* Remove hcache and bcache of newsgroup */
  4465. +void nntp_delete_group_cache (NNTP_DATA *nntp_data)
  4466. +{
  4467. + char file[_POSIX_PATH_MAX];
  4468. +
  4469. + if (!nntp_data || !nntp_data->nserv || !nntp_data->nserv->cacheable)
  4470. + return;
  4471. +
  4472. +#ifdef USE_HCACHE
  4473. + nntp_hcache_namer (nntp_data->group, file, sizeof (file));
  4474. + cache_expand (file, sizeof (file), &nntp_data->nserv->conn->account, file);
  4475. + unlink (file);
  4476. + nntp_data->lastCached = 0;
  4477. + dprint (2, (debugfile, "nntp_delete_group_cache: %s\n", file));
  4478. +#endif
  4479. +
  4480. + if (!nntp_data->bcache)
  4481. + nntp_data->bcache = mutt_bcache_open (&nntp_data->nserv->conn->account,
  4482. + nntp_data->group);
  4483. + if (nntp_data->bcache)
  4484. + {
  4485. + dprint (2, (debugfile, "nntp_delete_group_cache: %s/*\n", nntp_data->group));
  4486. + mutt_bcache_list (nntp_data->bcache, nntp_bcache_delete, NULL);
  4487. + mutt_bcache_close (&nntp_data->bcache);
  4488. + }
  4489. +}
  4490. +
  4491. +/* Remove hcache and bcache of all unexistent and unsubscribed newsgroups */
  4492. +void nntp_clear_cache (NNTP_SERVER *nserv)
  4493. +{
  4494. + char file[_POSIX_PATH_MAX];
  4495. + char *fp;
  4496. + struct dirent *entry;
  4497. + DIR *dp;
  4498. +
  4499. + if (!nserv || !nserv->cacheable)
  4500. + return;
  4501. +
  4502. + cache_expand (file, sizeof (file), &nserv->conn->account, NULL);
  4503. + dp = opendir (file);
  4504. + if (dp)
  4505. + {
  4506. + safe_strncat (file, sizeof (file), "/", 1);
  4507. + fp = file + strlen (file);
  4508. + while ((entry = readdir (dp)))
  4509. + {
  4510. + char *group = entry->d_name;
  4511. + struct stat sb;
  4512. + NNTP_DATA *nntp_data;
  4513. + NNTP_DATA nntp_tmp;
  4514. +
  4515. + if (mutt_strcmp (group, ".") == 0 ||
  4516. + mutt_strcmp (group, "..") == 0)
  4517. + continue;
  4518. + *fp = '\0';
  4519. + safe_strncat (file, sizeof (file), group, strlen (group));
  4520. + if (stat (file, &sb))
  4521. + continue;
  4522. +
  4523. +#ifdef USE_HCACHE
  4524. + if (S_ISREG (sb.st_mode))
  4525. + {
  4526. + char *ext = group + strlen (group) - 7;
  4527. + if (strlen (group) < 8 || mutt_strcmp (ext, ".hcache"))
  4528. + continue;
  4529. + *ext = '\0';
  4530. + }
  4531. + else
  4532. +#endif
  4533. + if (!S_ISDIR (sb.st_mode))
  4534. + continue;
  4535. +
  4536. + nntp_data = hash_find (nserv->groups_hash, group);
  4537. + if (!nntp_data)
  4538. + {
  4539. + nntp_data = &nntp_tmp;
  4540. + nntp_data->nserv = nserv;
  4541. + nntp_data->group = group;
  4542. + nntp_data->bcache = NULL;
  4543. + }
  4544. + else if (nntp_data->newsrc_ent || nntp_data->subscribed ||
  4545. + option (OPTSAVEUNSUB))
  4546. + continue;
  4547. +
  4548. + nntp_delete_group_cache (nntp_data);
  4549. + if (S_ISDIR (sb.st_mode))
  4550. + {
  4551. + rmdir (file);
  4552. + dprint (2, (debugfile, "nntp_clear_cache: %s\n", file));
  4553. + }
  4554. + }
  4555. + closedir (dp);
  4556. + }
  4557. + return;
  4558. +}
  4559. +
  4560. +/* %a = account url
  4561. + * %p = port
  4562. + * %P = port if specified
  4563. + * %s = news server name
  4564. + * %S = url schema
  4565. + * %u = username */
  4566. +const char *
  4567. +nntp_format_str (char *dest, size_t destlen, size_t col, char op, const char *src,
  4568. + const char *fmt, const char *ifstring, const char *elsestring,
  4569. + unsigned long data, format_flag flags)
  4570. +{
  4571. + NNTP_SERVER *nserv = (NNTP_SERVER *)data;
  4572. + ACCOUNT *acct = &nserv->conn->account;
  4573. + ciss_url_t url;
  4574. + char fn[SHORT_STRING], tmp[SHORT_STRING], *p;
  4575. +
  4576. + switch (op)
  4577. + {
  4578. + case 'a':
  4579. + mutt_account_tourl (acct, &url);
  4580. + url_ciss_tostring (&url, fn, sizeof (fn), U_PATH);
  4581. + p = strchr (fn, '/');
  4582. + if (p)
  4583. + *p = '\0';
  4584. + snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
  4585. + snprintf (dest, destlen, tmp, fn);
  4586. + break;
  4587. + case 'p':
  4588. + snprintf (tmp, sizeof (tmp), "%%%su", fmt);
  4589. + snprintf (dest, destlen, tmp, acct->port);
  4590. + break;
  4591. + case 'P':
  4592. + *dest = '\0';
  4593. + if (acct->flags & M_ACCT_PORT)
  4594. + {
  4595. + snprintf (tmp, sizeof (tmp), "%%%su", fmt);
  4596. + snprintf (dest, destlen, tmp, acct->port);
  4597. + }
  4598. + break;
  4599. + case 's':
  4600. + strncpy (fn, acct->host, sizeof (fn) - 1);
  4601. + mutt_strlower (fn);
  4602. + snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
  4603. + snprintf (dest, destlen, tmp, fn);
  4604. + break;
  4605. + case 'S':
  4606. + mutt_account_tourl (acct, &url);
  4607. + url_ciss_tostring (&url, fn, sizeof (fn), U_PATH);
  4608. + p = strchr (fn, ':');
  4609. + if (p)
  4610. + *p = '\0';
  4611. + snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
  4612. + snprintf (dest, destlen, tmp, fn);
  4613. + break;
  4614. + case 'u':
  4615. + snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
  4616. + snprintf (dest, destlen, tmp, acct->user);
  4617. + break;
  4618. + }
  4619. + return (src);
  4620. +}
  4621. +
  4622. +/* Automatically loads a newsrc into memory, if necessary.
  4623. + * Checks the size/mtime of a newsrc file, if it doesn't match, load
  4624. + * again. Hmm, if a system has broken mtimes, this might mean the file
  4625. + * is reloaded every time, which we'd have to fix. */
  4626. +NNTP_SERVER *nntp_select_server (char *server, int leave_lock)
  4627. +{
  4628. + char file[_POSIX_PATH_MAX];
  4629. + char *p;
  4630. + int rc;
  4631. + struct stat sb;
  4632. + ACCOUNT acct;
  4633. + NNTP_SERVER *nserv;
  4634. + NNTP_DATA *nntp_data;
  4635. + CONNECTION *conn;
  4636. + ciss_url_t url;
  4637. +
  4638. + if (!server || !*server)
  4639. + {
  4640. + mutt_error _("No news server defined!");
  4641. + mutt_sleep (2);
  4642. + return NULL;
  4643. + }
  4644. +
  4645. + /* create account from news server url */
  4646. + acct.flags = 0;
  4647. + acct.port = NNTP_PORT;
  4648. + acct.type = M_ACCT_TYPE_NNTP;
  4649. + snprintf (file, sizeof (file), "%s%s",
  4650. + strstr (server, "://") ? "" : "news://", server);
  4651. + if (url_parse_ciss (&url, file) < 0 ||
  4652. + (url.path && *url.path) ||
  4653. + !(url.scheme == U_NNTP || url.scheme == U_NNTPS) ||
  4654. + mutt_account_fromurl (&acct, &url) < 0)
  4655. + {
  4656. + mutt_error (_("%s is an invalid news server specification!"), server);
  4657. + mutt_sleep (2);
  4658. + return NULL;
  4659. + }
  4660. + if (url.scheme == U_NNTPS)
  4661. + {
  4662. + acct.flags |= M_ACCT_SSL;
  4663. + acct.port = NNTP_SSL_PORT;
  4664. + }
  4665. +
  4666. + /* find connection by account */
  4667. + conn = mutt_conn_find (NULL, &acct);
  4668. + if (!conn)
  4669. + return NULL;
  4670. + if (!(conn->account.flags & M_ACCT_USER) && acct.flags & M_ACCT_USER)
  4671. + {
  4672. + conn->account.flags |= M_ACCT_USER;
  4673. + conn->account.user[0] = '\0';
  4674. + }
  4675. +
  4676. + /* news server already exists */
  4677. + nserv = conn->data;
  4678. + if (nserv)
  4679. + {
  4680. + if (nserv->status == NNTP_BYE)
  4681. + nserv->status = NNTP_NONE;
  4682. + if (nntp_open_connection (nserv) < 0)
  4683. + return NULL;
  4684. +
  4685. + rc = nntp_newsrc_parse (nserv);
  4686. + if (rc < 0)
  4687. + return NULL;
  4688. +
  4689. + /* check for new newsgroups */
  4690. + if (!leave_lock && nntp_check_new_groups (nserv) < 0)
  4691. + rc = -1;
  4692. +
  4693. + /* .newsrc has been externally modified */
  4694. + if (rc > 0)
  4695. + nntp_clear_cache (nserv);
  4696. + if (rc < 0 || !leave_lock)
  4697. + nntp_newsrc_close (nserv);
  4698. + return rc < 0 ? NULL : nserv;
  4699. + }
  4700. +
  4701. + /* new news server */
  4702. + nserv = safe_calloc (1, sizeof (NNTP_SERVER));
  4703. + nserv->conn = conn;
  4704. + nserv->groups_hash = hash_create (1009, 0);
  4705. + nserv->groups_max = 16;
  4706. + nserv->groups_list = safe_malloc (nserv->groups_max * sizeof (nntp_data));
  4707. +
  4708. + rc = nntp_open_connection (nserv);
  4709. +
  4710. + /* try to create cache directory and enable caching */
  4711. + nserv->cacheable = 0;
  4712. + if (rc >= 0 && NewsCacheDir && *NewsCacheDir)
  4713. + {
  4714. + cache_expand (file, sizeof (file), &conn->account, NULL);
  4715. + p = *file == '/' ? file + 1 : file;
  4716. + while (1)
  4717. + {
  4718. + p = strchr (p, '/');
  4719. + if (p)
  4720. + *p = '\0';
  4721. + if ((stat (file, &sb) || (sb.st_mode & S_IFDIR) == 0) &&
  4722. + mkdir (file, 0700))
  4723. + {
  4724. + mutt_error (_("Can't create %s: %s."), file, strerror (errno));
  4725. + mutt_sleep (2);
  4726. + break;
  4727. + }
  4728. + if (!p)
  4729. + {
  4730. + nserv->cacheable = 1;
  4731. + break;
  4732. + }
  4733. + *p++ = '/';
  4734. + }
  4735. + }
  4736. +
  4737. + /* load .newsrc */
  4738. + if (rc >= 0)
  4739. + {
  4740. + mutt_FormatString (file, sizeof (file), 0, NONULL (NewsRc),
  4741. + nntp_format_str, (unsigned long)nserv, 0);
  4742. + mutt_expand_path (file, sizeof (file));
  4743. + nserv->newsrc_file = safe_strdup (file);
  4744. + rc = nntp_newsrc_parse (nserv);
  4745. + }
  4746. + if (rc >= 0)
  4747. + {
  4748. + /* try to load list of newsgroups from cache */
  4749. + if (nserv->cacheable && active_get_cache (nserv) == 0)
  4750. + rc = nntp_check_new_groups (nserv);
  4751. +
  4752. + /* load list of newsgroups from server */
  4753. + else
  4754. + rc = nntp_active_fetch (nserv);
  4755. + }
  4756. +
  4757. + if (rc >= 0)
  4758. + nntp_clear_cache (nserv);
  4759. +
  4760. +#ifdef USE_HCACHE
  4761. + /* check cache files */
  4762. + if (rc >= 0 && nserv->cacheable)
  4763. + {
  4764. + struct dirent *entry;
  4765. + DIR *dp = opendir (file);
  4766. +
  4767. + if (dp)
  4768. + {
  4769. + while ((entry = readdir (dp)))
  4770. + {
  4771. + header_cache_t *hc;
  4772. + void *hdata;
  4773. + char *group = entry->d_name;
  4774. +
  4775. + p = group + strlen (group) - 7;
  4776. + if (strlen (group) < 8 || strcmp (p, ".hcache"))
  4777. + continue;
  4778. + *p = '\0';
  4779. + nntp_data = hash_find (nserv->groups_hash, group);
  4780. + if (!nntp_data)
  4781. + continue;
  4782. +
  4783. + hc = nntp_hcache_open (nntp_data);
  4784. + if (!hc)
  4785. + continue;
  4786. +
  4787. + /* fetch previous values of first and last */
  4788. + hdata = mutt_hcache_fetch_raw (hc, "index", strlen);
  4789. + if (hdata)
  4790. + {
  4791. + anum_t first, last;
  4792. +
  4793. + if (sscanf (hdata, ANUM " " ANUM, &first, &last) == 2)
  4794. + {
  4795. + if (nntp_data->deleted)
  4796. + {
  4797. + nntp_data->firstMessage = first;
  4798. + nntp_data->lastMessage = last;
  4799. + }
  4800. + if (last >= nntp_data->firstMessage &&
  4801. + last <= nntp_data->lastMessage)
  4802. + {
  4803. + nntp_data->lastCached = last;
  4804. + dprint (2, (debugfile, "nntp_select_server: %s lastCached=%u\n",
  4805. + nntp_data->group, last));
  4806. + }
  4807. + }
  4808. + }
  4809. + mutt_hcache_close (hc);
  4810. + }
  4811. + closedir (dp);
  4812. + }
  4813. + }
  4814. +#endif
  4815. +
  4816. + if (rc < 0 || !leave_lock)
  4817. + nntp_newsrc_close (nserv);
  4818. +
  4819. + if (rc < 0)
  4820. + {
  4821. + hash_destroy (&nserv->groups_hash, nntp_data_free);
  4822. + FREE (&nserv->groups_list);
  4823. + FREE (&nserv->newsrc_file);
  4824. + FREE (&nserv->authenticators);
  4825. + FREE (&nserv);
  4826. + mutt_socket_close (conn);
  4827. + mutt_socket_free (conn);
  4828. + return NULL;
  4829. + }
  4830. +
  4831. + conn->data = nserv;
  4832. + return nserv;
  4833. +}
  4834. +
  4835. +/* Full status flags are not supported by nntp, but we can fake some of them:
  4836. + * Read = a read message number is in the .newsrc
  4837. + * New = not read and not cached
  4838. + * Old = not read but cached */
  4839. +void nntp_article_status (CONTEXT *ctx, HEADER *hdr, char *group, anum_t anum)
  4840. +{
  4841. + NNTP_DATA *nntp_data = ctx->data;
  4842. + unsigned int i;
  4843. +
  4844. + if (group)
  4845. + nntp_data = hash_find (nntp_data->nserv->groups_hash, group);
  4846. +
  4847. + if (!nntp_data)
  4848. + return;
  4849. +
  4850. + for (i = 0; i < nntp_data->newsrc_len; i++)
  4851. + {
  4852. + if ((anum >= nntp_data->newsrc_ent[i].first) &&
  4853. + (anum <= nntp_data->newsrc_ent[i].last))
  4854. + {
  4855. + /* can't use mutt_set_flag() because mx_update_context()
  4856. + didn't called yet */
  4857. + hdr->read = 1;
  4858. + return;
  4859. + }
  4860. + }
  4861. +
  4862. + /* article was not cached yet, it's new */
  4863. + if (anum > nntp_data->lastCached)
  4864. + return;
  4865. +
  4866. + /* article isn't read but cached, it's old */
  4867. + if (option (OPTMARKOLD))
  4868. + hdr->old = 1;
  4869. +}
  4870. +
  4871. +/* calculate number of unread articles using .newsrc data */
  4872. +void nntp_group_unread_stat (NNTP_DATA *nntp_data)
  4873. +{
  4874. + unsigned int i;
  4875. + anum_t first, last;
  4876. +
  4877. + nntp_data->unread = 0;
  4878. + if (nntp_data->lastMessage == 0 ||
  4879. + nntp_data->firstMessage > nntp_data->lastMessage)
  4880. + return;
  4881. +
  4882. + nntp_data->unread = nntp_data->lastMessage - nntp_data->firstMessage + 1;
  4883. + for (i = 0; i < nntp_data->newsrc_len; i++)
  4884. + {
  4885. + first = nntp_data->newsrc_ent[i].first;
  4886. + if (first < nntp_data->firstMessage)
  4887. + first = nntp_data->firstMessage;
  4888. + last = nntp_data->newsrc_ent[i].last;
  4889. + if (last > nntp_data->lastMessage)
  4890. + last = nntp_data->lastMessage;
  4891. + if (first <= last)
  4892. + nntp_data->unread -= last - first + 1;
  4893. + }
  4894. +}
  4895. +
  4896. +/* Subscribe newsgroup */
  4897. +NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *nserv, char *group)
  4898. +{
  4899. + NNTP_DATA *nntp_data;
  4900. +
  4901. + if (!nserv || !nserv->groups_hash || !group || !*group)
  4902. + return NULL;
  4903. +
  4904. + nntp_data = nntp_data_find (nserv, group);
  4905. + nntp_data->subscribed = 1;
  4906. + if (!nntp_data->newsrc_ent)
  4907. + {
  4908. + nntp_data->newsrc_ent = safe_calloc (1, sizeof (NEWSRC_ENTRY));
  4909. + nntp_data->newsrc_len = 1;
  4910. + nntp_data->newsrc_ent[0].first = 1;
  4911. + nntp_data->newsrc_ent[0].last = 0;
  4912. + }
  4913. + return nntp_data;
  4914. +}
  4915. +
  4916. +/* Unsubscribe newsgroup */
  4917. +NNTP_DATA *mutt_newsgroup_unsubscribe (NNTP_SERVER *nserv, char *group)
  4918. +{
  4919. + NNTP_DATA *nntp_data;
  4920. +
  4921. + if (!nserv || !nserv->groups_hash || !group || !*group)
  4922. + return NULL;
  4923. +
  4924. + nntp_data = hash_find (nserv->groups_hash, group);
  4925. + if (!nntp_data)
  4926. + return NULL;
  4927. +
  4928. + nntp_data->subscribed = 0;
  4929. + if (!option (OPTSAVEUNSUB))
  4930. + {
  4931. + nntp_data->newsrc_len = 0;
  4932. + FREE (&nntp_data->newsrc_ent);
  4933. + }
  4934. + return nntp_data;
  4935. +}
  4936. +
  4937. +/* Catchup newsgroup */
  4938. +NNTP_DATA *mutt_newsgroup_catchup (NNTP_SERVER *nserv, char *group)
  4939. +{
  4940. + NNTP_DATA *nntp_data;
  4941. +
  4942. + if (!nserv || !nserv->groups_hash || !group || !*group)
  4943. + return NULL;
  4944. +
  4945. + nntp_data = hash_find (nserv->groups_hash, group);
  4946. + if (!nntp_data)
  4947. + return NULL;
  4948. +
  4949. + if (nntp_data->newsrc_ent)
  4950. + {
  4951. + safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
  4952. + nntp_data->newsrc_len = 1;
  4953. + nntp_data->newsrc_ent[0].first = 1;
  4954. + nntp_data->newsrc_ent[0].last = nntp_data->lastMessage;
  4955. + }
  4956. + nntp_data->unread = 0;
  4957. + if (Context && Context->data == nntp_data)
  4958. + {
  4959. + unsigned int i;
  4960. +
  4961. + for (i = 0; i < Context->msgcount; i++)
  4962. + mutt_set_flag (Context, Context->hdrs[i], M_READ, 1);
  4963. + }
  4964. + return nntp_data;
  4965. +}
  4966. +
  4967. +/* Uncatchup newsgroup */
  4968. +NNTP_DATA *mutt_newsgroup_uncatchup (NNTP_SERVER *nserv, char *group)
  4969. +{
  4970. + NNTP_DATA *nntp_data;
  4971. +
  4972. + if (!nserv || !nserv->groups_hash || !group || !*group)
  4973. + return NULL;
  4974. +
  4975. + nntp_data = hash_find (nserv->groups_hash, group);
  4976. + if (!nntp_data)
  4977. + return NULL;
  4978. +
  4979. + if (nntp_data->newsrc_ent)
  4980. + {
  4981. + safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
  4982. + nntp_data->newsrc_len = 1;
  4983. + nntp_data->newsrc_ent[0].first = 1;
  4984. + nntp_data->newsrc_ent[0].last = nntp_data->firstMessage - 1;
  4985. + }
  4986. + if (Context && Context->data == nntp_data)
  4987. + {
  4988. + unsigned int i;
  4989. +
  4990. + nntp_data->unread = Context->msgcount;
  4991. + for (i = 0; i < Context->msgcount; i++)
  4992. + mutt_set_flag (Context, Context->hdrs[i], M_READ, 0);
  4993. + }
  4994. + else
  4995. + nntp_data->unread = nntp_data->lastMessage - nntp_data->newsrc_ent[0].last;
  4996. + return nntp_data;
  4997. +}
  4998. +
  4999. +/* Get first newsgroup with new messages */
  5000. +void nntp_buffy (char *buf, size_t len)
  5001. +{
  5002. + unsigned int i;
  5003. +
  5004. + for (i = 0; i < CurrentNewsSrv->groups_num; i++)
  5005. + {
  5006. + NNTP_DATA *nntp_data = CurrentNewsSrv->groups_list[i];
  5007. +
  5008. + if (!nntp_data || !nntp_data->subscribed || !nntp_data->unread)
  5009. + continue;
  5010. +
  5011. + if (Context && Context->magic == M_NNTP &&
  5012. + !mutt_strcmp (nntp_data->group, ((NNTP_DATA *)Context->data)->group))
  5013. + {
  5014. + unsigned int i, unread = 0;
  5015. +
  5016. + for (i = 0; i < Context->msgcount; i++)
  5017. + if (!Context->hdrs[i]->read && !Context->hdrs[i]->deleted)
  5018. + unread++;
  5019. + if (!unread)
  5020. + continue;
  5021. + }
  5022. + strfcpy (buf, nntp_data->group, len);
  5023. + break;
  5024. + }
  5025. +}
  5026. diff --git a/nntp.c b/nntp.c
  5027. new file mode 100644
  5028. index 0000000..c78d3fa
  5029. --- /dev/null
  5030. +++ b/nntp.c
  5031. @@ -0,0 +1,2404 @@
  5032. +/*
  5033. + * Copyright (C) 1998 Brandon Long <blong@fiction.net>
  5034. + * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
  5035. + * Copyright (C) 2000-2012 Vsevolod Volkov <vvv@mutt.org.ua>
  5036. + *
  5037. + * This program is free software; you can redistribute it and/or modify
  5038. + * it under the terms of the GNU General Public License as published by
  5039. + * the Free Software Foundation; either version 2 of the License, or
  5040. + * (at your option) any later version.
  5041. + *
  5042. + * This program is distributed in the hope that it will be useful,
  5043. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  5044. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  5045. + * GNU General Public License for more details.
  5046. + *
  5047. + * You should have received a copy of the GNU General Public License
  5048. + * along with this program; if not, write to the Free Software
  5049. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  5050. + */
  5051. +
  5052. +#if HAVE_CONFIG_H
  5053. +#include "config.h"
  5054. +#endif
  5055. +
  5056. +#include "mutt.h"
  5057. +#include "mutt_curses.h"
  5058. +#include "sort.h"
  5059. +#include "mx.h"
  5060. +#include "mime.h"
  5061. +#include "rfc1524.h"
  5062. +#include "rfc2047.h"
  5063. +#include "mailbox.h"
  5064. +#include "mutt_crypt.h"
  5065. +#include "nntp.h"
  5066. +
  5067. +#if defined(USE_SSL)
  5068. +#include "mutt_ssl.h"
  5069. +#endif
  5070. +
  5071. +#ifdef HAVE_PGP
  5072. +#include "pgp.h"
  5073. +#endif
  5074. +
  5075. +#ifdef HAVE_SMIME
  5076. +#include "smime.h"
  5077. +#endif
  5078. +
  5079. +#if USE_HCACHE
  5080. +#include "hcache.h"
  5081. +#endif
  5082. +
  5083. +#include <unistd.h>
  5084. +#include <string.h>
  5085. +#include <ctype.h>
  5086. +#include <stdlib.h>
  5087. +
  5088. +#ifdef USE_SASL
  5089. +#include <sasl/sasl.h>
  5090. +#include <sasl/saslutil.h>
  5091. +
  5092. +#include "mutt_sasl.h"
  5093. +#endif
  5094. +
  5095. +static int nntp_connect_error (NNTP_SERVER *nserv)
  5096. +{
  5097. + nserv->status = NNTP_NONE;
  5098. + mutt_error _("Server closed connection!");
  5099. + mutt_sleep (2);
  5100. + return -1;
  5101. +}
  5102. +
  5103. +/* Get capabilities:
  5104. + * -1 - error, connection is closed
  5105. + * 0 - mode is reader, capabilities setted up
  5106. + * 1 - need to switch to reader mode */
  5107. +static int nntp_capabilities (NNTP_SERVER *nserv)
  5108. +{
  5109. + CONNECTION *conn = nserv->conn;
  5110. + unsigned int mode_reader = 0;
  5111. + char buf[LONG_STRING];
  5112. + char authinfo[LONG_STRING] = "";
  5113. +
  5114. + nserv->hasCAPABILITIES = 0;
  5115. + nserv->hasSTARTTLS = 0;
  5116. + nserv->hasDATE = 0;
  5117. + nserv->hasLIST_NEWSGROUPS = 0;
  5118. + nserv->hasLISTGROUP = 0;
  5119. + nserv->hasOVER = 0;
  5120. + FREE (&nserv->authenticators);
  5121. +
  5122. + if (mutt_socket_write (conn, "CAPABILITIES\r\n") < 0 ||
  5123. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5124. + return nntp_connect_error (nserv);
  5125. +
  5126. + /* no capabilities */
  5127. + if (mutt_strncmp ("101", buf, 3))
  5128. + return 1;
  5129. + nserv->hasCAPABILITIES = 1;
  5130. +
  5131. + /* parse capabilities */
  5132. + do
  5133. + {
  5134. + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5135. + return nntp_connect_error (nserv);
  5136. + if (!mutt_strcmp ("STARTTLS", buf))
  5137. + nserv->hasSTARTTLS = 1;
  5138. + else if (!mutt_strcmp ("MODE-READER", buf))
  5139. + mode_reader = 1;
  5140. + else if (!mutt_strcmp ("READER", buf))
  5141. + {
  5142. + nserv->hasDATE = 1;
  5143. + nserv->hasLISTGROUP = 1;
  5144. + }
  5145. + else if (!mutt_strncmp ("AUTHINFO ", buf, 9))
  5146. + {
  5147. + safe_strcat (buf, sizeof (buf), " ");
  5148. + strfcpy (authinfo, buf + 8, sizeof (authinfo));
  5149. + }
  5150. +#ifdef USE_SASL
  5151. + else if (!mutt_strncmp ("SASL ", buf, 5))
  5152. + {
  5153. + char *p = buf + 5;
  5154. + while (*p == ' ')
  5155. + p++;
  5156. + nserv->authenticators = safe_strdup (p);
  5157. + }
  5158. +#endif
  5159. + else if (!mutt_strcmp ("OVER", buf))
  5160. + nserv->hasOVER = 1;
  5161. + else if (!mutt_strncmp ("LIST ", buf, 5))
  5162. + {
  5163. + char *p = strstr (buf, " NEWSGROUPS");
  5164. + if (p)
  5165. + {
  5166. + p += 11;
  5167. + if (*p == '\0' || *p == ' ')
  5168. + nserv->hasLIST_NEWSGROUPS = 1;
  5169. + }
  5170. + }
  5171. + } while (mutt_strcmp (".", buf));
  5172. + *buf = '\0';
  5173. +#ifdef USE_SASL
  5174. + if (nserv->authenticators && strcasestr (authinfo, " SASL "))
  5175. + strfcpy (buf, nserv->authenticators, sizeof (buf));
  5176. +#endif
  5177. + if (strcasestr (authinfo, " USER "))
  5178. + {
  5179. + if (*buf)
  5180. + safe_strcat (buf, sizeof (buf), " ");
  5181. + safe_strcat (buf, sizeof (buf), "USER");
  5182. + }
  5183. + mutt_str_replace (&nserv->authenticators, buf);
  5184. +
  5185. + /* current mode is reader */
  5186. + if (nserv->hasLISTGROUP)
  5187. + return 0;
  5188. +
  5189. + /* server is mode-switching, need to switch to reader mode */
  5190. + if (mode_reader)
  5191. + return 1;
  5192. +
  5193. + mutt_socket_close (conn);
  5194. + nserv->status = NNTP_BYE;
  5195. + mutt_error _("Server doesn't support reader mode.");
  5196. + mutt_sleep (2);
  5197. + return -1;
  5198. +}
  5199. +
  5200. +char *OverviewFmt =
  5201. + "Subject:\0"
  5202. + "From:\0"
  5203. + "Date:\0"
  5204. + "Message-ID:\0"
  5205. + "References:\0"
  5206. + "Content-Length:\0"
  5207. + "Lines:\0"
  5208. + "\0";
  5209. +
  5210. +/* Detect supported commands */
  5211. +static int nntp_attempt_features (NNTP_SERVER *nserv)
  5212. +{
  5213. + CONNECTION *conn = nserv->conn;
  5214. + char buf[LONG_STRING];
  5215. +
  5216. + /* no CAPABILITIES, trying DATE, LISTGROUP, LIST NEWSGROUPS */
  5217. + if (!nserv->hasCAPABILITIES)
  5218. + {
  5219. + if (mutt_socket_write (conn, "DATE\r\n") < 0 ||
  5220. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5221. + return nntp_connect_error (nserv);
  5222. + if (mutt_strncmp ("500", buf, 3))
  5223. + nserv->hasDATE = 1;
  5224. +
  5225. + if (mutt_socket_write (conn, "LISTGROUP\r\n") < 0 ||
  5226. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5227. + return nntp_connect_error (nserv);
  5228. + if (mutt_strncmp ("500", buf, 3))
  5229. + nserv->hasLISTGROUP = 1;
  5230. +
  5231. + if (mutt_socket_write (conn, "LIST NEWSGROUPS +\r\n") < 0 ||
  5232. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5233. + return nntp_connect_error (nserv);
  5234. + if (mutt_strncmp ("500", buf, 3))
  5235. + nserv->hasLIST_NEWSGROUPS = 1;
  5236. + if (!mutt_strncmp ("215", buf, 3))
  5237. + {
  5238. + do
  5239. + {
  5240. + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5241. + return nntp_connect_error (nserv);
  5242. + } while (mutt_strcmp (".", buf));
  5243. + }
  5244. + }
  5245. +
  5246. + /* no LIST NEWSGROUPS, trying XGTITLE */
  5247. + if (!nserv->hasLIST_NEWSGROUPS)
  5248. + {
  5249. + if (mutt_socket_write (conn, "XGTITLE\r\n") < 0 ||
  5250. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5251. + return nntp_connect_error (nserv);
  5252. + if (mutt_strncmp ("500", buf, 3))
  5253. + nserv->hasXGTITLE = 1;
  5254. + }
  5255. +
  5256. + /* no OVER, trying XOVER */
  5257. + if (!nserv->hasOVER)
  5258. + {
  5259. + if (mutt_socket_write (conn, "XOVER\r\n") < 0 ||
  5260. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5261. + return nntp_connect_error (nserv);
  5262. + if (mutt_strncmp ("500", buf, 3))
  5263. + nserv->hasXOVER = 1;
  5264. + }
  5265. +
  5266. + /* trying LIST OVERVIEW.FMT */
  5267. + if (nserv->hasOVER || nserv->hasXOVER)
  5268. + {
  5269. + if (mutt_socket_write (conn, "LIST OVERVIEW.FMT\r\n") < 0 ||
  5270. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5271. + return nntp_connect_error (nserv);
  5272. + if (mutt_strncmp ("215", buf, 3))
  5273. + nserv->overview_fmt = OverviewFmt;
  5274. + else
  5275. + {
  5276. + int chunk, cont = 0;
  5277. + size_t buflen = 2 * LONG_STRING, off = 0, b = 0;
  5278. +
  5279. + if (nserv->overview_fmt)
  5280. + FREE (&nserv->overview_fmt);
  5281. + nserv->overview_fmt = safe_malloc (buflen);
  5282. +
  5283. + while (1)
  5284. + {
  5285. + if (buflen - off < LONG_STRING)
  5286. + {
  5287. + buflen *= 2;
  5288. + safe_realloc (&nserv->overview_fmt, buflen);
  5289. + }
  5290. +
  5291. + chunk = mutt_socket_readln (nserv->overview_fmt + off,
  5292. + buflen - off, conn);
  5293. + if (chunk < 0)
  5294. + {
  5295. + FREE (&nserv->overview_fmt);
  5296. + return nntp_connect_error (nserv);
  5297. + }
  5298. +
  5299. + if (!cont && !mutt_strcmp (".", nserv->overview_fmt + off))
  5300. + break;
  5301. +
  5302. + cont = chunk >= buflen - off ? 1 : 0;
  5303. + off += strlen (nserv->overview_fmt + off);
  5304. + if (!cont)
  5305. + {
  5306. + char *colon;
  5307. +
  5308. + if (nserv->overview_fmt[b] == ':')
  5309. + {
  5310. + memmove (nserv->overview_fmt + b,
  5311. + nserv->overview_fmt + b + 1, off - b - 1);
  5312. + nserv->overview_fmt[off - 1] = ':';
  5313. + }
  5314. + colon = strchr (nserv->overview_fmt + b, ':');
  5315. + if (!colon)
  5316. + nserv->overview_fmt[off++] = ':';
  5317. + else if (strcmp (colon + 1, "full"))
  5318. + off = colon + 1 - nserv->overview_fmt;
  5319. + if (!strcasecmp (nserv->overview_fmt + b, "Bytes:"))
  5320. + {
  5321. + strcpy (nserv->overview_fmt + b, "Content-Length:");
  5322. + off = b + strlen (nserv->overview_fmt + b);
  5323. + }
  5324. + nserv->overview_fmt[off++] = '\0';
  5325. + b = off;
  5326. + }
  5327. + }
  5328. + nserv->overview_fmt[off++] = '\0';
  5329. + safe_realloc (&nserv->overview_fmt, off);
  5330. + }
  5331. + }
  5332. + return 0;
  5333. +}
  5334. +
  5335. +/* Get login, password and authenticate */
  5336. +static int nntp_auth (NNTP_SERVER *nserv)
  5337. +{
  5338. + CONNECTION *conn = nserv->conn;
  5339. + char buf[LONG_STRING];
  5340. + char authenticators[LONG_STRING] = "USER";
  5341. + char *method, *a, *p;
  5342. + unsigned char flags = conn->account.flags;
  5343. +
  5344. + while (1)
  5345. + {
  5346. + /* get login and password */
  5347. + if (mutt_account_getuser (&conn->account) || !conn->account.user[0] ||
  5348. + mutt_account_getpass (&conn->account) || !conn->account.pass[0])
  5349. + break;
  5350. +
  5351. + /* get list of authenticators */
  5352. + if (NntpAuthenticators && *NntpAuthenticators)
  5353. + strfcpy (authenticators, NntpAuthenticators, sizeof (authenticators));
  5354. + else if (nserv->hasCAPABILITIES)
  5355. + {
  5356. + strfcpy (authenticators, NONULL (nserv->authenticators),
  5357. + sizeof (authenticators));
  5358. + p = authenticators;
  5359. + while (*p)
  5360. + {
  5361. + if (*p == ' ')
  5362. + *p = ':';
  5363. + p++;
  5364. + }
  5365. + }
  5366. + p = authenticators;
  5367. + while (*p)
  5368. + {
  5369. + *p = ascii_toupper (*p);
  5370. + p++;
  5371. + }
  5372. +
  5373. + dprint (1, (debugfile,
  5374. + "nntp_auth: available methods: %s\n", nserv->authenticators));
  5375. + a = authenticators;
  5376. + while (1)
  5377. + {
  5378. + if (!a)
  5379. + {
  5380. + mutt_error _("No authenticators available");
  5381. + mutt_sleep (2);
  5382. + break;
  5383. + }
  5384. +
  5385. + method = a;
  5386. + a = strchr (a, ':');
  5387. + if (a)
  5388. + *a++ = '\0';
  5389. +
  5390. + /* check authenticator */
  5391. + if (nserv->hasCAPABILITIES)
  5392. + {
  5393. + char *m;
  5394. +
  5395. + if (!nserv->authenticators)
  5396. + continue;
  5397. + m = strcasestr (nserv->authenticators, method);
  5398. + if (!m)
  5399. + continue;
  5400. + if (m > nserv->authenticators && *(m - 1) != ' ')
  5401. + continue;
  5402. + m += strlen (method);
  5403. + if (*m != '\0' && *m != ' ')
  5404. + continue;
  5405. + }
  5406. + dprint (1, (debugfile, "nntp_auth: trying method %s\n", method));
  5407. +
  5408. + /* AUTHINFO USER authentication */
  5409. + if (!strcmp (method, "USER"))
  5410. + {
  5411. + mutt_message (_("Authenticating (%s)..."), method);
  5412. + snprintf (buf, sizeof (buf), "AUTHINFO USER %s\r\n", conn->account.user);
  5413. + if (mutt_socket_write (conn, buf) < 0 ||
  5414. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5415. + break;
  5416. +
  5417. + /* authenticated, password is not required */
  5418. + if (!mutt_strncmp ("281", buf, 3))
  5419. + return 0;
  5420. +
  5421. + /* username accepted, sending password */
  5422. + if (!mutt_strncmp ("381", buf, 3))
  5423. + {
  5424. +#ifdef DEBUG
  5425. + if (debuglevel < M_SOCK_LOG_FULL)
  5426. + dprint (M_SOCK_LOG_CMD, (debugfile,
  5427. + "%d> AUTHINFO PASS *\n", conn->fd));
  5428. +#endif
  5429. + snprintf (buf, sizeof (buf), "AUTHINFO PASS %s\r\n",
  5430. + conn->account.pass);
  5431. + if (mutt_socket_write_d (conn, buf, -1, M_SOCK_LOG_FULL) < 0 ||
  5432. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5433. + break;
  5434. +
  5435. + /* authenticated */
  5436. + if (!mutt_strncmp ("281", buf, 3))
  5437. + return 0;
  5438. + }
  5439. +
  5440. + /* server doesn't support AUTHINFO USER, trying next method */
  5441. + if (*buf == '5')
  5442. + continue;
  5443. + }
  5444. +
  5445. + else
  5446. + {
  5447. +#ifdef USE_SASL
  5448. + sasl_conn_t *saslconn;
  5449. + sasl_interact_t *interaction = NULL;
  5450. + int rc;
  5451. + char inbuf[LONG_STRING] = "";
  5452. + const char *mech;
  5453. + const char *client_out = NULL;
  5454. + unsigned int client_len, len;
  5455. +
  5456. + if (mutt_sasl_client_new (conn, &saslconn) < 0)
  5457. + {
  5458. + dprint (1, (debugfile,
  5459. + "nntp_auth: error allocating SASL connection.\n"));
  5460. + continue;
  5461. + }
  5462. +
  5463. + while (1)
  5464. + {
  5465. + rc = sasl_client_start (saslconn, method, &interaction,
  5466. + &client_out, &client_len, &mech);
  5467. + if (rc != SASL_INTERACT)
  5468. + break;
  5469. + mutt_sasl_interact (interaction);
  5470. + }
  5471. + if (rc != SASL_OK && rc != SASL_CONTINUE)
  5472. + {
  5473. + sasl_dispose (&saslconn);
  5474. + dprint (1, (debugfile,
  5475. + "nntp_auth: error starting SASL authentication exchange.\n"));
  5476. + continue;
  5477. + }
  5478. +
  5479. + mutt_message (_("Authenticating (%s)..."), method);
  5480. + snprintf (buf, sizeof (buf), "AUTHINFO SASL %s", method);
  5481. +
  5482. + /* looping protocol */
  5483. + while (rc == SASL_CONTINUE || (rc == SASL_OK && client_len))
  5484. + {
  5485. + /* send out client response */
  5486. + if (client_len)
  5487. + {
  5488. +#ifdef DEBUG
  5489. + if (debuglevel >= M_SOCK_LOG_FULL)
  5490. + {
  5491. + char tmp[LONG_STRING];
  5492. + memcpy (tmp, client_out, client_len);
  5493. + for (p = tmp; p < tmp + client_len; p++)
  5494. + {
  5495. + if (*p == '\0')
  5496. + *p = '.';
  5497. + }
  5498. + *p = '\0';
  5499. + dprint (1, (debugfile, "SASL> %s\n", tmp));
  5500. + }
  5501. +#endif
  5502. +
  5503. + if (*buf)
  5504. + safe_strcat (buf, sizeof (buf), " ");
  5505. + len = strlen (buf);
  5506. + if (sasl_encode64 (client_out, client_len,
  5507. + buf + len, sizeof (buf) - len, &len) != SASL_OK)
  5508. + {
  5509. + dprint (1, (debugfile,
  5510. + "nntp_auth: error base64-encoding client response.\n"));
  5511. + break;
  5512. + }
  5513. + }
  5514. +
  5515. + safe_strcat (buf, sizeof (buf), "\r\n");
  5516. +#ifdef DEBUG
  5517. + if (debuglevel < M_SOCK_LOG_FULL)
  5518. + {
  5519. + if (strchr (buf, ' '))
  5520. + dprint (M_SOCK_LOG_CMD, (debugfile, "%d> AUTHINFO SASL %s%s\n",
  5521. + conn->fd, method, client_len ? " sasl_data" : ""));
  5522. + else
  5523. + dprint (M_SOCK_LOG_CMD, (debugfile, "%d> sasl_data\n", conn->fd));
  5524. + }
  5525. +#endif
  5526. + client_len = 0;
  5527. + if (mutt_socket_write_d (conn, buf, -1, M_SOCK_LOG_FULL) < 0 ||
  5528. + mutt_socket_readln_d (inbuf, sizeof (inbuf), conn, M_SOCK_LOG_FULL) < 0)
  5529. + break;
  5530. + if (mutt_strncmp (inbuf, "283 ", 4) &&
  5531. + mutt_strncmp (inbuf, "383 ", 4))
  5532. + {
  5533. +#ifdef DEBUG
  5534. + if (debuglevel < M_SOCK_LOG_FULL)
  5535. + dprint (M_SOCK_LOG_CMD, (debugfile, "%d< %s\n", conn->fd, inbuf));
  5536. +#endif
  5537. + break;
  5538. + }
  5539. +#ifdef DEBUG
  5540. + if (debuglevel < M_SOCK_LOG_FULL)
  5541. + {
  5542. + inbuf[3] = '\0';
  5543. + dprint (M_SOCK_LOG_CMD, (debugfile,
  5544. + "%d< %s sasl_data\n", conn->fd, inbuf));
  5545. + }
  5546. +#endif
  5547. +
  5548. + if (!strcmp ("=", inbuf + 4))
  5549. + len = 0;
  5550. + else if (sasl_decode64 (inbuf + 4, strlen (inbuf + 4),
  5551. + buf, sizeof (buf) - 1, &len) != SASL_OK)
  5552. + {
  5553. + dprint (1, (debugfile,
  5554. + "nntp_auth: error base64-decoding server response.\n"));
  5555. + break;
  5556. + }
  5557. +#ifdef DEBUG
  5558. + else if (debuglevel >= M_SOCK_LOG_FULL)
  5559. + {
  5560. + char tmp[LONG_STRING];
  5561. + memcpy (tmp, buf, len);
  5562. + for (p = tmp; p < tmp + len; p++)
  5563. + {
  5564. + if (*p == '\0')
  5565. + *p = '.';
  5566. + }
  5567. + *p = '\0';
  5568. + dprint (1, (debugfile, "SASL< %s\n", tmp));
  5569. + }
  5570. +#endif
  5571. +
  5572. + while (1)
  5573. + {
  5574. + rc = sasl_client_step (saslconn, buf, len,
  5575. + &interaction, &client_out, &client_len);
  5576. + if (rc != SASL_INTERACT)
  5577. + break;
  5578. + mutt_sasl_interact (interaction);
  5579. + }
  5580. + if (*inbuf != '3')
  5581. + break;
  5582. +
  5583. + *buf = '\0';
  5584. + } /* looping protocol */
  5585. +
  5586. + if (rc == SASL_OK && client_len == 0 && *inbuf == '2')
  5587. + {
  5588. + mutt_sasl_setup_conn (conn, saslconn);
  5589. + return 0;
  5590. + }
  5591. +
  5592. + /* terminate SASL sessoin */
  5593. + sasl_dispose (&saslconn);
  5594. + if (conn->fd < 0)
  5595. + break;
  5596. + if (!mutt_strncmp (inbuf, "383 ", 4))
  5597. + {
  5598. + if (mutt_socket_write (conn, "*\r\n") < 0 ||
  5599. + mutt_socket_readln (inbuf, sizeof (inbuf), conn) < 0)
  5600. + break;
  5601. + }
  5602. +
  5603. + /* server doesn't support AUTHINFO SASL, trying next method */
  5604. + if (*inbuf == '5')
  5605. + continue;
  5606. +#else
  5607. + continue;
  5608. +#endif /* USE_SASL */
  5609. + }
  5610. +
  5611. + mutt_error (_("%s authentication failed."), method);
  5612. + mutt_sleep (2);
  5613. + break;
  5614. + }
  5615. + break;
  5616. + }
  5617. +
  5618. + /* error */
  5619. + nserv->status = NNTP_BYE;
  5620. + conn->account.flags = flags;
  5621. + if (conn->fd < 0)
  5622. + {
  5623. + mutt_error _("Server closed connection!");
  5624. + mutt_sleep (2);
  5625. + }
  5626. + else
  5627. + mutt_socket_close (conn);
  5628. + return -1;
  5629. +}
  5630. +
  5631. +/* Connect to server, authenticate and get capabilities */
  5632. +int nntp_open_connection (NNTP_SERVER *nserv)
  5633. +{
  5634. + CONNECTION *conn = nserv->conn;
  5635. + char buf[STRING];
  5636. + int cap;
  5637. + unsigned int posting = 0, auth = 1;
  5638. +
  5639. + if (nserv->status == NNTP_OK)
  5640. + return 0;
  5641. + if (nserv->status == NNTP_BYE)
  5642. + return -1;
  5643. + nserv->status = NNTP_NONE;
  5644. +
  5645. + if (mutt_socket_open (conn) < 0)
  5646. + return -1;
  5647. +
  5648. + if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5649. + return nntp_connect_error (nserv);
  5650. +
  5651. + if (!mutt_strncmp ("200", buf, 3))
  5652. + posting = 1;
  5653. + else if (mutt_strncmp ("201", buf, 3))
  5654. + {
  5655. + mutt_socket_close (conn);
  5656. + mutt_remove_trailing_ws (buf);
  5657. + mutt_error ("%s", buf);
  5658. + mutt_sleep (2);
  5659. + return -1;
  5660. + }
  5661. +
  5662. + /* get initial capabilities */
  5663. + cap = nntp_capabilities (nserv);
  5664. + if (cap < 0)
  5665. + return -1;
  5666. +
  5667. + /* tell news server to switch to mode reader if it isn't so */
  5668. + if (cap > 0)
  5669. + {
  5670. + if (mutt_socket_write (conn, "MODE READER\r\n") < 0 ||
  5671. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5672. + return nntp_connect_error (nserv);
  5673. +
  5674. + if (!mutt_strncmp ("200", buf, 3))
  5675. + posting = 1;
  5676. + else if (!mutt_strncmp ("201", buf, 3))
  5677. + posting = 0;
  5678. + /* error if has capabilities, ignore result if no capabilities */
  5679. + else if (nserv->hasCAPABILITIES)
  5680. + {
  5681. + mutt_socket_close (conn);
  5682. + mutt_error _("Could not switch to reader mode.");
  5683. + mutt_sleep (2);
  5684. + return -1;
  5685. + }
  5686. +
  5687. + /* recheck capabilities after MODE READER */
  5688. + if (nserv->hasCAPABILITIES)
  5689. + {
  5690. + cap = nntp_capabilities (nserv);
  5691. + if (cap < 0)
  5692. + return -1;
  5693. + }
  5694. + }
  5695. +
  5696. + mutt_message (_("Connected to %s. %s"), conn->account.host,
  5697. + posting ? _("Posting is ok.") : _("Posting is NOT ok."));
  5698. + mutt_sleep (1);
  5699. +
  5700. +#if defined(USE_SSL)
  5701. + /* Attempt STARTTLS if available and desired. */
  5702. + if (nserv->use_tls != 1 && (nserv->hasSTARTTLS || option (OPTSSLFORCETLS)))
  5703. + {
  5704. + if (nserv->use_tls == 0)
  5705. + nserv->use_tls = option (OPTSSLFORCETLS) ||
  5706. + query_quadoption (OPT_SSLSTARTTLS,
  5707. + _("Secure connection with TLS?")) == M_YES ? 2 : 1;
  5708. + if (nserv->use_tls == 2)
  5709. + {
  5710. + if (mutt_socket_write (conn, "STARTTLS\r\n") < 0 ||
  5711. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5712. + return nntp_connect_error (nserv);
  5713. + if (mutt_strncmp ("382", buf, 3))
  5714. + {
  5715. + nserv->use_tls = 0;
  5716. + mutt_error ("STARTTLS: %s", buf);
  5717. + mutt_sleep (2);
  5718. + }
  5719. + else if (mutt_ssl_starttls (conn))
  5720. + {
  5721. + nserv->use_tls = 0;
  5722. + nserv->status = NNTP_NONE;
  5723. + mutt_socket_close (nserv->conn);
  5724. + mutt_error _("Could not negotiate TLS connection");
  5725. + mutt_sleep (2);
  5726. + return -1;
  5727. + }
  5728. + else
  5729. + {
  5730. + /* recheck capabilities after STARTTLS */
  5731. + cap = nntp_capabilities (nserv);
  5732. + if (cap < 0)
  5733. + return -1;
  5734. + }
  5735. + }
  5736. + }
  5737. +#endif
  5738. +
  5739. + /* authentication required? */
  5740. + if (conn->account.flags & M_ACCT_USER)
  5741. + {
  5742. + if (!conn->account.user[0])
  5743. + auth = 0;
  5744. + }
  5745. + else
  5746. + {
  5747. + if (mutt_socket_write (conn, "STAT\r\n") < 0 ||
  5748. + mutt_socket_readln (buf, sizeof (buf), conn) < 0)
  5749. + return nntp_connect_error (nserv);
  5750. + if (mutt_strncmp ("480", buf, 3))
  5751. + auth = 0;
  5752. + }
  5753. +
  5754. + /* authenticate */
  5755. + if (auth && nntp_auth (nserv) < 0)
  5756. + return -1;
  5757. +
  5758. + /* get final capabilities after authentication */
  5759. + if (nserv->hasCAPABILITIES && (auth || cap > 0))
  5760. + {
  5761. + cap = nntp_capabilities (nserv);
  5762. + if (cap < 0)
  5763. + return -1;
  5764. + if (cap > 0)
  5765. + {
  5766. + mutt_socket_close (conn);
  5767. + mutt_error _("Could not switch to reader mode.");
  5768. + mutt_sleep (2);
  5769. + return -1;
  5770. + }
  5771. + }
  5772. +
  5773. + /* attempt features */
  5774. + if (nntp_attempt_features (nserv) < 0)
  5775. + return -1;
  5776. +
  5777. + nserv->status = NNTP_OK;
  5778. + return 0;
  5779. +}
  5780. +
  5781. +/* Send data from buffer and receive answer to same buffer */
  5782. +static int nntp_query (NNTP_DATA *nntp_data, char *line, size_t linelen)
  5783. +{
  5784. + NNTP_SERVER *nserv = nntp_data->nserv;
  5785. + char buf[LONG_STRING];
  5786. +
  5787. + if (nserv->status == NNTP_BYE)
  5788. + return -1;
  5789. +
  5790. + while (1)
  5791. + {
  5792. + if (nserv->status == NNTP_OK)
  5793. + {
  5794. + int rc = 0;
  5795. +
  5796. + if (*line)
  5797. + rc = mutt_socket_write (nserv->conn, line);
  5798. + else if (nntp_data->group)
  5799. + {
  5800. + snprintf (buf, sizeof (buf), "GROUP %s\r\n", nntp_data->group);
  5801. + rc = mutt_socket_write (nserv->conn, buf);
  5802. + }
  5803. + if (rc >= 0)
  5804. + rc = mutt_socket_readln (buf, sizeof (buf), nserv->conn);
  5805. + if (rc >= 0)
  5806. + break;
  5807. + }
  5808. +
  5809. + /* reconnect */
  5810. + while (1)
  5811. + {
  5812. + nserv->status = NNTP_NONE;
  5813. + if (nntp_open_connection (nserv) == 0)
  5814. + break;
  5815. +
  5816. + snprintf (buf, sizeof (buf), _("Connection to %s lost. Reconnect?"),
  5817. + nserv->conn->account.host);
  5818. + if (mutt_yesorno (buf, M_YES) != M_YES)
  5819. + {
  5820. + nserv->status = NNTP_BYE;
  5821. + return -1;
  5822. + }
  5823. + }
  5824. +
  5825. + /* select newsgroup after reconnection */
  5826. + if (nntp_data->group)
  5827. + {
  5828. + snprintf (buf, sizeof (buf), "GROUP %s\r\n", nntp_data->group);
  5829. + if (mutt_socket_write (nserv->conn, buf) < 0 ||
  5830. + mutt_socket_readln (buf, sizeof (buf), nserv->conn) < 0)
  5831. + return nntp_connect_error (nserv);
  5832. + }
  5833. + if (!*line)
  5834. + break;
  5835. + }
  5836. +
  5837. + strfcpy (line, buf, linelen);
  5838. + return 0;
  5839. +}
  5840. +
  5841. +/* This function calls funct(*line, *data) for each received line,
  5842. + * funct(NULL, *data) if rewind(*data) needs, exits when fail or done:
  5843. + * 0 - success
  5844. + * 1 - bad response (answer in query buffer)
  5845. + * -1 - conection lost
  5846. + * -2 - error in funct(*line, *data) */
  5847. +static int nntp_fetch_lines (NNTP_DATA *nntp_data, char *query, size_t qlen,
  5848. + char *msg, int (*funct) (char *, void *), void *data)
  5849. +{
  5850. + int done = FALSE;
  5851. + int rc;
  5852. +
  5853. + while (!done)
  5854. + {
  5855. + char buf[LONG_STRING];
  5856. + char *line;
  5857. + unsigned int lines = 0;
  5858. + size_t off = 0;
  5859. + progress_t progress;
  5860. +
  5861. + if (msg)
  5862. + mutt_progress_init (&progress, msg, M_PROGRESS_MSG, ReadInc, -1);
  5863. +
  5864. + strfcpy (buf, query, sizeof (buf));
  5865. + if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
  5866. + return -1;
  5867. + if (buf[0] != '2')
  5868. + {
  5869. + strfcpy (query, buf, qlen);
  5870. + return 1;
  5871. + }
  5872. +
  5873. + line = safe_malloc (sizeof (buf));
  5874. + rc = 0;
  5875. +
  5876. + while (1)
  5877. + {
  5878. + char *p;
  5879. + int chunk = mutt_socket_readln_d (buf, sizeof (buf),
  5880. + nntp_data->nserv->conn, M_SOCK_LOG_HDR);
  5881. + if (chunk < 0)
  5882. + {
  5883. + nntp_data->nserv->status = NNTP_NONE;
  5884. + break;
  5885. + }
  5886. +
  5887. + p = buf;
  5888. + if (!off && buf[0] == '.')
  5889. + {
  5890. + if (buf[1] == '\0')
  5891. + {
  5892. + done = TRUE;
  5893. + break;
  5894. + }
  5895. + if (buf[1] == '.')
  5896. + p++;
  5897. + }
  5898. +
  5899. + strfcpy (line + off, p, sizeof (buf));
  5900. +
  5901. + if (chunk >= sizeof (buf))
  5902. + off += strlen (p);
  5903. + else
  5904. + {
  5905. + if (msg)
  5906. + mutt_progress_update (&progress, ++lines, -1);
  5907. +
  5908. + if (rc == 0 && funct (line, data) < 0)
  5909. + rc = -2;
  5910. + off = 0;
  5911. + }
  5912. +
  5913. + safe_realloc (&line, off + sizeof (buf));
  5914. + }
  5915. + FREE (&line);
  5916. + funct (NULL, data);
  5917. + }
  5918. + return rc;
  5919. +}
  5920. +
  5921. +/* Parse newsgroup description */
  5922. +static int fetch_description (char *line, void *data)
  5923. +{
  5924. + NNTP_SERVER *nserv = data;
  5925. + NNTP_DATA *nntp_data;
  5926. + char *desc;
  5927. +
  5928. + if (!line)
  5929. + return 0;
  5930. +
  5931. + desc = strpbrk (line, " \t");
  5932. + if (desc)
  5933. + {
  5934. + *desc++ = '\0';
  5935. + desc += strspn (desc, " \t");
  5936. + }
  5937. + else
  5938. + desc = strchr (line, '\0');
  5939. +
  5940. + nntp_data = hash_find (nserv->groups_hash, line);
  5941. + if (nntp_data && mutt_strcmp (desc, nntp_data->desc))
  5942. + {
  5943. + mutt_str_replace (&nntp_data->desc, desc);
  5944. + dprint (2, (debugfile, "group: %s, desc: %s\n", line, desc));
  5945. + }
  5946. + return 0;
  5947. +}
  5948. +
  5949. +/* Fetch newsgroups descriptions.
  5950. + * Returns the same code as nntp_fetch_lines() */
  5951. +static int get_description (NNTP_DATA *nntp_data, char *wildmat, char *msg)
  5952. +{
  5953. + NNTP_SERVER *nserv;
  5954. + char buf[STRING];
  5955. + char *cmd;
  5956. + int rc;
  5957. +
  5958. + /* get newsgroup description, if possible */
  5959. + nserv = nntp_data->nserv;
  5960. + if (!wildmat)
  5961. + wildmat = nntp_data->group;
  5962. + if (nserv->hasLIST_NEWSGROUPS)
  5963. + cmd = "LIST NEWSGROUPS";
  5964. + else if (nserv->hasXGTITLE)
  5965. + cmd = "XGTITLE";
  5966. + else
  5967. + return 0;
  5968. +
  5969. + snprintf (buf, sizeof (buf), "%s %s\r\n", cmd, wildmat);
  5970. + rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), msg,
  5971. + fetch_description, nserv);
  5972. + if (rc > 0)
  5973. + {
  5974. + mutt_error ("%s: %s", cmd, buf);
  5975. + mutt_sleep (2);
  5976. + }
  5977. + return rc;
  5978. +}
  5979. +
  5980. +/* Update read flag and set article number if empty */
  5981. +static void nntp_parse_xref (CONTEXT *ctx, HEADER *hdr)
  5982. +{
  5983. + NNTP_DATA *nntp_data = ctx->data;
  5984. + char *buf, *p;
  5985. +
  5986. + buf = p = safe_strdup (hdr->env->xref);
  5987. + while (p)
  5988. + {
  5989. + char *grp, *colon;
  5990. + anum_t anum;
  5991. +
  5992. + /* skip to next word */
  5993. + p += strspn (p, " \t");
  5994. + grp = p;
  5995. +
  5996. + /* skip to end of word */
  5997. + p = strpbrk (p, " \t");
  5998. + if (p)
  5999. + *p++ = '\0';
  6000. +
  6001. + /* find colon */
  6002. + colon = strchr (grp, ':');
  6003. + if (!colon)
  6004. + continue;
  6005. + *colon++ = '\0';
  6006. + if (sscanf (colon, ANUM, &anum) != 1)
  6007. + continue;
  6008. +
  6009. + nntp_article_status (ctx, hdr, grp, anum);
  6010. + if (hdr && !NHDR (hdr)->article_num && !mutt_strcmp (nntp_data->group, grp))
  6011. + NHDR (hdr)->article_num = anum;
  6012. + }
  6013. + FREE (&buf);
  6014. +}
  6015. +
  6016. +/* Write line to temporarily file */
  6017. +static int fetch_tempfile (char *line, void *data)
  6018. +{
  6019. + FILE *fp = data;
  6020. +
  6021. + if (!line)
  6022. + rewind (fp);
  6023. + else if (fputs (line, fp) == EOF || fputc ('\n', fp) == EOF)
  6024. + return -1;
  6025. + return 0;
  6026. +}
  6027. +
  6028. +typedef struct
  6029. +{
  6030. + CONTEXT *ctx;
  6031. + anum_t first;
  6032. + anum_t last;
  6033. + int restore;
  6034. + unsigned char *messages;
  6035. + progress_t progress;
  6036. +#ifdef USE_HCACHE
  6037. + header_cache_t *hc;
  6038. +#endif
  6039. +} FETCH_CTX;
  6040. +
  6041. +/* Parse article number */
  6042. +static int fetch_numbers (char *line, void *data)
  6043. +{
  6044. + FETCH_CTX *fc = data;
  6045. + anum_t anum;
  6046. +
  6047. + if (!line)
  6048. + return 0;
  6049. + if (sscanf (line, ANUM, &anum) != 1)
  6050. + return 0;
  6051. + if (anum < fc->first || anum > fc->last)
  6052. + return 0;
  6053. + fc->messages[anum - fc->first] = 1;
  6054. + return 0;
  6055. +}
  6056. +
  6057. +/* Parse overview line */
  6058. +static int parse_overview_line (char *line, void *data)
  6059. +{
  6060. + FETCH_CTX *fc = data;
  6061. + CONTEXT *ctx = fc->ctx;
  6062. + NNTP_DATA *nntp_data = ctx->data;
  6063. + HEADER *hdr;
  6064. + FILE *fp;
  6065. + char tempfile[_POSIX_PATH_MAX];
  6066. + char *header, *field;
  6067. + int save = 1;
  6068. + anum_t anum;
  6069. +
  6070. + if (!line)
  6071. + return 0;
  6072. +
  6073. + /* parse article number */
  6074. + field = strchr (line, '\t');
  6075. + if (field)
  6076. + *field++ = '\0';
  6077. + if (sscanf (line, ANUM, &anum) != 1)
  6078. + return 0;
  6079. + dprint (2, (debugfile, "parse_overview_line: " ANUM "\n", anum));
  6080. +
  6081. + /* out of bounds */
  6082. + if (anum < fc->first || anum > fc->last)
  6083. + return 0;
  6084. +
  6085. + /* not in LISTGROUP */
  6086. + if (!fc->messages[anum - fc->first])
  6087. + {
  6088. + /* progress */
  6089. + if (!ctx->quiet)
  6090. + mutt_progress_update (&fc->progress, anum - fc->first + 1, -1);
  6091. + return 0;
  6092. + }
  6093. +
  6094. + /* convert overview line to header */
  6095. + mutt_mktemp (tempfile, sizeof (tempfile));
  6096. + fp = safe_fopen (tempfile, "w+");
  6097. + if (!fp)
  6098. + return -1;
  6099. +
  6100. + header = nntp_data->nserv->overview_fmt;
  6101. + while (field)
  6102. + {
  6103. + char *b = field;
  6104. +
  6105. + if (*header)
  6106. + {
  6107. + if (strstr (header, ":full") == NULL && fputs (header, fp) == EOF)
  6108. + {
  6109. + fclose (fp);
  6110. + unlink (tempfile);
  6111. + return -1;
  6112. + }
  6113. + header = strchr (header, '\0') + 1;
  6114. + }
  6115. +
  6116. + field = strchr (field, '\t');
  6117. + if (field)
  6118. + *field++ = '\0';
  6119. + if (fputs (b, fp) == EOF || fputc ('\n', fp) == EOF)
  6120. + {
  6121. + fclose (fp);
  6122. + unlink (tempfile);
  6123. + return -1;
  6124. + }
  6125. + }
  6126. + rewind (fp);
  6127. +
  6128. + /* allocate memory for headers */
  6129. + if (ctx->msgcount >= ctx->hdrmax)
  6130. + mx_alloc_memory (ctx);
  6131. +
  6132. + /* parse header */
  6133. + hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
  6134. + hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
  6135. + hdr->env->newsgroups = safe_strdup (nntp_data->group);
  6136. + hdr->received = hdr->date_sent;
  6137. + fclose (fp);
  6138. + unlink (tempfile);
  6139. +
  6140. +#ifdef USE_HCACHE
  6141. + if (fc->hc)
  6142. + {
  6143. + void *hdata;
  6144. + char buf[16];
  6145. +
  6146. + /* try to replace with header from cache */
  6147. + snprintf (buf, sizeof (buf), "%d", anum);
  6148. + hdata = mutt_hcache_fetch (fc->hc, buf, strlen);
  6149. + if (hdata)
  6150. + {
  6151. + dprint (2, (debugfile,
  6152. + "parse_overview_line: mutt_hcache_fetch %s\n", buf));
  6153. + mutt_free_header (&hdr);
  6154. + ctx->hdrs[ctx->msgcount] =
  6155. + hdr = mutt_hcache_restore (hdata, NULL);
  6156. + hdr->read = 0;
  6157. + hdr->old = 0;
  6158. +
  6159. + /* skip header marked as deleted in cache */
  6160. + if (hdr->deleted && !fc->restore)
  6161. + {
  6162. + if (nntp_data->bcache)
  6163. + {
  6164. + dprint (2, (debugfile,
  6165. + "parse_overview_line: mutt_bcache_del %s\n", buf));
  6166. + mutt_bcache_del (nntp_data->bcache, buf);
  6167. + }
  6168. + save = 0;
  6169. + }
  6170. + }
  6171. +
  6172. + /* not chached yet, store header */
  6173. + else
  6174. + {
  6175. + dprint (2, (debugfile,
  6176. + "parse_overview_line: mutt_hcache_store %s\n", buf));
  6177. + mutt_hcache_store (fc->hc, buf, hdr, 0, strlen, M_GENERATE_UIDVALIDITY);
  6178. + }
  6179. + }
  6180. +#endif
  6181. +
  6182. + if (save)
  6183. + {
  6184. + hdr->index = ctx->msgcount++;
  6185. + hdr->read = 0;
  6186. + hdr->old = 0;
  6187. + hdr->deleted = 0;
  6188. + hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
  6189. + NHDR (hdr)->article_num = anum;
  6190. + if (fc->restore)
  6191. + hdr->changed = 1;
  6192. + else
  6193. + {
  6194. + nntp_article_status (ctx, hdr, NULL, anum);
  6195. + if (!hdr->read)
  6196. + nntp_parse_xref (ctx, hdr);
  6197. + }
  6198. + if (anum > nntp_data->lastLoaded)
  6199. + nntp_data->lastLoaded = anum;
  6200. + }
  6201. + else
  6202. + mutt_free_header (&hdr);
  6203. +
  6204. + /* progress */
  6205. + if (!ctx->quiet)
  6206. + mutt_progress_update (&fc->progress, anum - fc->first + 1, -1);
  6207. + return 0;
  6208. +}
  6209. +
  6210. +/* Fetch headers */
  6211. +static int nntp_fetch_headers (CONTEXT *ctx, void *hc,
  6212. + anum_t first, anum_t last, int restore)
  6213. +{
  6214. + NNTP_DATA *nntp_data = ctx->data;
  6215. + FETCH_CTX fc;
  6216. + HEADER *hdr;
  6217. + char buf[HUGE_STRING];
  6218. + int rc = 0;
  6219. + int oldmsgcount = ctx->msgcount;
  6220. + anum_t current;
  6221. +#ifdef USE_HCACHE
  6222. + void *hdata;
  6223. +#endif
  6224. +
  6225. + /* if empty group or nothing to do */
  6226. + if (!last || first > last)
  6227. + return 0;
  6228. +
  6229. + /* init fetch context */
  6230. + fc.ctx = ctx;
  6231. + fc.first = first;
  6232. + fc.last = last;
  6233. + fc.restore = restore;
  6234. + fc.messages = safe_calloc (last - first + 1, sizeof (unsigned char));
  6235. +#ifdef USE_HCACHE
  6236. + fc.hc = hc;
  6237. +#endif
  6238. +
  6239. + /* fetch list of articles */
  6240. + if (nntp_data->nserv->hasLISTGROUP && !nntp_data->deleted)
  6241. + {
  6242. + if (!ctx->quiet)
  6243. + mutt_message _("Fetching list of articles...");
  6244. + snprintf (buf, sizeof (buf), "LISTGROUP %s\r\n", nntp_data->group);
  6245. + rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
  6246. + fetch_numbers, &fc);
  6247. + if (rc > 0)
  6248. + {
  6249. + mutt_error ("LISTGROUP: %s", buf);
  6250. + mutt_sleep (2);
  6251. + }
  6252. + if (rc == 0)
  6253. + {
  6254. + for (current = first; current <= last && rc == 0; current++)
  6255. + {
  6256. + if (fc.messages[current - first])
  6257. + continue;
  6258. +
  6259. + snprintf (buf, sizeof (buf), "%d", current);
  6260. + if (nntp_data->bcache)
  6261. + {
  6262. + dprint (2, (debugfile,
  6263. + "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
  6264. + mutt_bcache_del (nntp_data->bcache, buf);
  6265. + }
  6266. +
  6267. +#ifdef USE_HCACHE
  6268. + if (fc.hc)
  6269. + {
  6270. + dprint (2, (debugfile,
  6271. + "nntp_fetch_headers: mutt_hcache_delete %s\n", buf));
  6272. + mutt_hcache_delete (fc.hc, buf, strlen);
  6273. + }
  6274. +#endif
  6275. + }
  6276. + }
  6277. + }
  6278. + else
  6279. + for (current = first; current <= last; current++)
  6280. + fc.messages[current - first] = 1;
  6281. +
  6282. + /* fetching header from cache or server, or fallback to fetch overview */
  6283. + if (!ctx->quiet)
  6284. + mutt_progress_init (&fc.progress, _("Fetching message headers..."),
  6285. + M_PROGRESS_MSG, ReadInc, last - first + 1);
  6286. + for (current = first; current <= last && rc == 0; current++)
  6287. + {
  6288. + if (!ctx->quiet)
  6289. + mutt_progress_update (&fc.progress, current - first + 1, -1);
  6290. +
  6291. +#ifdef USE_HCACHE
  6292. + snprintf (buf, sizeof (buf), "%d", current);
  6293. +#endif
  6294. +
  6295. + /* delete header from cache that does not exist on server */
  6296. + if (!fc.messages[current - first])
  6297. + continue;
  6298. +
  6299. + /* allocate memory for headers */
  6300. + if (ctx->msgcount >= ctx->hdrmax)
  6301. + mx_alloc_memory (ctx);
  6302. +
  6303. +#ifdef USE_HCACHE
  6304. + /* try to fetch header from cache */
  6305. + hdata = mutt_hcache_fetch (fc.hc, buf, strlen);
  6306. + if (hdata)
  6307. + {
  6308. + dprint (2, (debugfile,
  6309. + "nntp_fetch_headers: mutt_hcache_fetch %s\n", buf));
  6310. + ctx->hdrs[ctx->msgcount] =
  6311. + hdr = mutt_hcache_restore (hdata, NULL);
  6312. +
  6313. + /* skip header marked as deleted in cache */
  6314. + if (hdr->deleted && !restore)
  6315. + {
  6316. + mutt_free_header (&hdr);
  6317. + if (nntp_data->bcache)
  6318. + {
  6319. + dprint (2, (debugfile,
  6320. + "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
  6321. + mutt_bcache_del (nntp_data->bcache, buf);
  6322. + }
  6323. + continue;
  6324. + }
  6325. +
  6326. + hdr->read = 0;
  6327. + hdr->old = 0;
  6328. + }
  6329. + else
  6330. +#endif
  6331. +
  6332. + /* don't try to fetch header from removed newsgroup */
  6333. + if (nntp_data->deleted)
  6334. + continue;
  6335. +
  6336. + /* fallback to fetch overview */
  6337. + else if (nntp_data->nserv->hasOVER || nntp_data->nserv->hasXOVER)
  6338. + break;
  6339. +
  6340. + /* fetch header from server */
  6341. + else
  6342. + {
  6343. + FILE *fp;
  6344. + char tempfile[_POSIX_PATH_MAX];
  6345. +
  6346. + mutt_mktemp (tempfile, sizeof (tempfile));
  6347. + fp = safe_fopen (tempfile, "w+");
  6348. + if (!fp)
  6349. + {
  6350. + mutt_perror (tempfile);
  6351. + mutt_sleep (2);
  6352. + unlink (tempfile);
  6353. + rc = -1;
  6354. + break;
  6355. + }
  6356. +
  6357. + snprintf (buf, sizeof (buf), "HEAD %d\r\n", current);
  6358. + rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
  6359. + fetch_tempfile, fp);
  6360. + if (rc)
  6361. + {
  6362. + fclose (fp);
  6363. + unlink (tempfile);
  6364. + if (rc < 0)
  6365. + break;
  6366. +
  6367. + /* invalid response */
  6368. + if (mutt_strncmp ("423", buf, 3))
  6369. + {
  6370. + mutt_error ("HEAD: %s", buf);
  6371. + mutt_sleep (2);
  6372. + break;
  6373. + }
  6374. +
  6375. + /* no such article */
  6376. + if (nntp_data->bcache)
  6377. + {
  6378. + snprintf (buf, sizeof (buf), "%d", current);
  6379. + dprint (2, (debugfile,
  6380. + "nntp_fetch_headers: mutt_bcache_del %s\n", buf));
  6381. + mutt_bcache_del (nntp_data->bcache, buf);
  6382. + }
  6383. + rc = 0;
  6384. + continue;
  6385. + }
  6386. +
  6387. + /* parse header */
  6388. + hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
  6389. + hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
  6390. + hdr->received = hdr->date_sent;
  6391. + fclose (fp);
  6392. + unlink (tempfile);
  6393. + }
  6394. +
  6395. + /* save header in context */
  6396. + hdr->index = ctx->msgcount++;
  6397. + hdr->read = 0;
  6398. + hdr->old = 0;
  6399. + hdr->deleted = 0;
  6400. + hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
  6401. + NHDR (hdr)->article_num = current;
  6402. + if (restore)
  6403. + hdr->changed = 1;
  6404. + else
  6405. + {
  6406. + nntp_article_status (ctx, hdr, NULL, NHDR (hdr)->article_num);
  6407. + if (!hdr->read)
  6408. + nntp_parse_xref (ctx, hdr);
  6409. + }
  6410. + if (current > nntp_data->lastLoaded)
  6411. + nntp_data->lastLoaded = current;
  6412. + }
  6413. +
  6414. + /* fetch overview information */
  6415. + if (current <= last && rc == 0) {
  6416. + char *cmd = nntp_data->nserv->hasOVER ? "OVER" : "XOVER";
  6417. + snprintf (buf, sizeof (buf), "%s %d-%d\r\n", cmd, current, last);
  6418. + rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
  6419. + parse_overview_line, &fc);
  6420. + if (rc > 0)
  6421. + {
  6422. + mutt_error ("%s: %s", cmd, buf);
  6423. + mutt_sleep (2);
  6424. + }
  6425. + }
  6426. +
  6427. + if (ctx->msgcount > oldmsgcount)
  6428. + mx_update_context (ctx, ctx->msgcount - oldmsgcount);
  6429. +
  6430. + FREE (&fc.messages);
  6431. + if (rc != 0)
  6432. + return -1;
  6433. + mutt_clear_error ();
  6434. + return 0;
  6435. +}
  6436. +
  6437. +/* Open newsgroup */
  6438. +int nntp_open_mailbox (CONTEXT *ctx)
  6439. +{
  6440. + NNTP_SERVER *nserv;
  6441. + NNTP_DATA *nntp_data;
  6442. + char buf[HUGE_STRING];
  6443. + char server[LONG_STRING];
  6444. + char *group;
  6445. + int rc;
  6446. + void *hc = NULL;
  6447. + anum_t first, last, count = 0;
  6448. + ciss_url_t url;
  6449. +
  6450. + strfcpy (buf, ctx->path, sizeof (buf));
  6451. + if (url_parse_ciss (&url, buf) < 0 || !url.path ||
  6452. + !(url.scheme == U_NNTP || url.scheme == U_NNTPS))
  6453. + {
  6454. + mutt_error (_("%s is an invalid newsgroup specification!"), ctx->path);
  6455. + mutt_sleep (2);
  6456. + return -1;
  6457. + }
  6458. +
  6459. + group = url.path;
  6460. + url.path = strchr (url.path, '\0');
  6461. + url_ciss_tostring (&url, server, sizeof (server), 0);
  6462. + nserv = nntp_select_server (server, 1);
  6463. + if (!nserv)
  6464. + return -1;
  6465. + CurrentNewsSrv = nserv;
  6466. +
  6467. + /* find news group data structure */
  6468. + nntp_data = hash_find (nserv->groups_hash, group);
  6469. + if (!nntp_data)
  6470. + {
  6471. + nntp_newsrc_close (nserv);
  6472. + mutt_error (_("Newsgroup %s not found on the server."), group);
  6473. + mutt_sleep (2);
  6474. + return -1;
  6475. + }
  6476. +
  6477. + mutt_bit_unset (ctx->rights, M_ACL_INSERT);
  6478. + if (!nntp_data->newsrc_ent && !nntp_data->subscribed &&
  6479. + !option (OPTSAVEUNSUB))
  6480. + ctx->readonly = 1;
  6481. +
  6482. + /* select newsgroup */
  6483. + mutt_message (_("Selecting %s..."), group);
  6484. + buf[0] = '\0';
  6485. + if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
  6486. + {
  6487. + nntp_newsrc_close (nserv);
  6488. + return -1;
  6489. + }
  6490. +
  6491. + /* newsgroup not found, remove it */
  6492. + if (!mutt_strncmp ("411", buf, 3))
  6493. + {
  6494. + mutt_error (_("Newsgroup %s has been removed from the server."),
  6495. + nntp_data->group);
  6496. + if (!nntp_data->deleted)
  6497. + {
  6498. + nntp_data->deleted = 1;
  6499. + nntp_active_save_cache (nserv);
  6500. + }
  6501. + if (nntp_data->newsrc_ent && !nntp_data->subscribed &&
  6502. + !option (OPTSAVEUNSUB))
  6503. + {
  6504. + FREE (&nntp_data->newsrc_ent);
  6505. + nntp_data->newsrc_len = 0;
  6506. + nntp_delete_group_cache (nntp_data);
  6507. + nntp_newsrc_update (nserv);
  6508. + }
  6509. + mutt_sleep (2);
  6510. + }
  6511. +
  6512. + /* parse newsgroup info */
  6513. + else {
  6514. + if (sscanf (buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
  6515. + {
  6516. + nntp_newsrc_close (nserv);
  6517. + mutt_error ("GROUP: %s", buf);
  6518. + mutt_sleep (2);
  6519. + return -1;
  6520. + }
  6521. + nntp_data->firstMessage = first;
  6522. + nntp_data->lastMessage = last;
  6523. + nntp_data->deleted = 0;
  6524. +
  6525. + /* get description if empty */
  6526. + if (option (OPTLOADDESC) && !nntp_data->desc)
  6527. + {
  6528. + if (get_description (nntp_data, NULL, NULL) < 0)
  6529. + {
  6530. + nntp_newsrc_close (nserv);
  6531. + return -1;
  6532. + }
  6533. + if (nntp_data->desc)
  6534. + nntp_active_save_cache (nserv);
  6535. + }
  6536. + }
  6537. +
  6538. + time (&nserv->check_time);
  6539. + ctx->data = nntp_data;
  6540. + ctx->mx_close = nntp_fastclose_mailbox;
  6541. + if (!nntp_data->bcache && (nntp_data->newsrc_ent ||
  6542. + nntp_data->subscribed || option (OPTSAVEUNSUB)))
  6543. + nntp_data->bcache = mutt_bcache_open (&nserv->conn->account,
  6544. + nntp_data->group);
  6545. +
  6546. + /* strip off extra articles if adding context is greater than $nntp_context */
  6547. + first = nntp_data->firstMessage;
  6548. + if (NntpContext && nntp_data->lastMessage - first + 1 > NntpContext)
  6549. + first = nntp_data->lastMessage - NntpContext + 1;
  6550. + nntp_data->lastLoaded = first ? first - 1 : 0;
  6551. + count = nntp_data->firstMessage;
  6552. + nntp_data->firstMessage = first;
  6553. + nntp_bcache_update (nntp_data);
  6554. + nntp_data->firstMessage = count;
  6555. +#ifdef USE_HCACHE
  6556. + hc = nntp_hcache_open (nntp_data);
  6557. + nntp_hcache_update (nntp_data, hc);
  6558. +#endif
  6559. + if (!hc)
  6560. + {
  6561. + mutt_bit_unset (ctx->rights, M_ACL_WRITE);
  6562. + mutt_bit_unset (ctx->rights, M_ACL_DELETE);
  6563. + }
  6564. + nntp_newsrc_close (nserv);
  6565. + rc = nntp_fetch_headers (ctx, hc, first, nntp_data->lastMessage, 0);
  6566. +#ifdef USE_HCACHE
  6567. + mutt_hcache_close (hc);
  6568. +#endif
  6569. + if (rc < 0)
  6570. + return -1;
  6571. + nntp_data->lastLoaded = nntp_data->lastMessage;
  6572. + nserv->newsrc_modified = 0;
  6573. + return 0;
  6574. +}
  6575. +
  6576. +/* Fetch message */
  6577. +int nntp_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
  6578. +{
  6579. + NNTP_DATA *nntp_data = ctx->data;
  6580. + NNTP_ACACHE *acache;
  6581. + HEADER *hdr = ctx->hdrs[msgno];
  6582. + char buf[_POSIX_PATH_MAX];
  6583. + char article[16];
  6584. + char *fetch_msg = _("Fetching message...");
  6585. + int rc;
  6586. +
  6587. + /* try to get article from cache */
  6588. + acache = &nntp_data->acache[hdr->index % NNTP_ACACHE_LEN];
  6589. + if (acache->path)
  6590. + {
  6591. + if (acache->index == hdr->index)
  6592. + {
  6593. + msg->fp = fopen (acache->path, "r");
  6594. + if (msg->fp)
  6595. + return 0;
  6596. + }
  6597. + /* clear previous entry */
  6598. + else
  6599. + {
  6600. + unlink (acache->path);
  6601. + FREE (&acache->path);
  6602. + }
  6603. + }
  6604. + snprintf (article, sizeof (article), "%d", NHDR (hdr)->article_num);
  6605. + msg->fp = mutt_bcache_get (nntp_data->bcache, article);
  6606. + if (msg->fp)
  6607. + {
  6608. + if (NHDR (hdr)->parsed)
  6609. + return 0;
  6610. + }
  6611. + else
  6612. + {
  6613. + /* don't try to fetch article from removed newsgroup */
  6614. + if (nntp_data->deleted)
  6615. + return -1;
  6616. +
  6617. + /* create new cache file */
  6618. + mutt_message (fetch_msg);
  6619. + msg->fp = mutt_bcache_put (nntp_data->bcache, article, 1);
  6620. + if (!msg->fp)
  6621. + {
  6622. + mutt_mktemp (buf, sizeof (buf));
  6623. + acache->path = safe_strdup (buf);
  6624. + acache->index = hdr->index;
  6625. + msg->fp = safe_fopen (acache->path, "w+");
  6626. + if (!msg->fp)
  6627. + {
  6628. + mutt_perror (acache->path);
  6629. + unlink (acache->path);
  6630. + FREE (&acache->path);
  6631. + return -1;
  6632. + }
  6633. + }
  6634. +
  6635. + /* fetch message to cache file */
  6636. + snprintf (buf, sizeof (buf), "ARTICLE %s\r\n",
  6637. + NHDR (hdr)->article_num ? article : hdr->env->message_id);
  6638. + rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), fetch_msg,
  6639. + fetch_tempfile, msg->fp);
  6640. + if (rc)
  6641. + {
  6642. + safe_fclose (&msg->fp);
  6643. + if (acache->path)
  6644. + {
  6645. + unlink (acache->path);
  6646. + FREE (&acache->path);
  6647. + }
  6648. + if (rc > 0)
  6649. + {
  6650. + if (!mutt_strncmp (NHDR (hdr)->article_num ? "423" : "430", buf, 3))
  6651. + mutt_error (_("Article %d not found on the server."),
  6652. + NHDR (hdr)->article_num ? article : hdr->env->message_id);
  6653. + else
  6654. + mutt_error ("ARTICLE: %s", buf);
  6655. + }
  6656. + return -1;
  6657. + }
  6658. +
  6659. + if (!acache->path)
  6660. + mutt_bcache_commit (nntp_data->bcache, article);
  6661. + }
  6662. +
  6663. + /* replace envelope with new one
  6664. + * hash elements must be updated because pointers will be changed */
  6665. + if (ctx->id_hash && hdr->env->message_id)
  6666. + hash_delete (ctx->id_hash, hdr->env->message_id, hdr, NULL);
  6667. + if (ctx->subj_hash && hdr->env->real_subj)
  6668. + hash_delete (ctx->subj_hash, hdr->env->real_subj, hdr, NULL);
  6669. +
  6670. + mutt_free_envelope (&hdr->env);
  6671. + hdr->env = mutt_read_rfc822_header (msg->fp, hdr, 0, 0);
  6672. +
  6673. + if (ctx->id_hash && hdr->env->message_id)
  6674. + hash_insert (ctx->id_hash, hdr->env->message_id, hdr, 0);
  6675. + if (ctx->subj_hash && hdr->env->real_subj)
  6676. + hash_insert (ctx->subj_hash, hdr->env->real_subj, hdr, 1);
  6677. +
  6678. + /* fix content length */
  6679. + fseek (msg->fp, 0, SEEK_END);
  6680. + hdr->content->length = ftell (msg->fp) - hdr->content->offset;
  6681. +
  6682. + /* this is called in mutt before the open which fetches the message,
  6683. + * which is probably wrong, but we just call it again here to handle
  6684. + * the problem instead of fixing it */
  6685. + NHDR (hdr)->parsed = 1;
  6686. + mutt_parse_mime_message (ctx, hdr);
  6687. +
  6688. + /* these would normally be updated in mx_update_context(), but the
  6689. + * full headers aren't parsed with overview, so the information wasn't
  6690. + * available then */
  6691. + if (WithCrypto)
  6692. + hdr->security = crypt_query (hdr->content);
  6693. +
  6694. + rewind (msg->fp);
  6695. + mutt_clear_error();
  6696. + return 0;
  6697. +}
  6698. +
  6699. +/* Post article */
  6700. +int nntp_post (const char *msg) {
  6701. + NNTP_DATA *nntp_data, nntp_tmp;
  6702. + FILE *fp;
  6703. + char buf[LONG_STRING];
  6704. + size_t len;
  6705. +
  6706. + if (Context && Context->magic == M_NNTP)
  6707. + nntp_data = Context->data;
  6708. + else
  6709. + {
  6710. + CurrentNewsSrv = nntp_select_server (NewsServer, 0);
  6711. + if (!CurrentNewsSrv)
  6712. + return -1;
  6713. +
  6714. + nntp_data = &nntp_tmp;
  6715. + nntp_data->nserv = CurrentNewsSrv;
  6716. + nntp_data->group = NULL;
  6717. + }
  6718. +
  6719. + fp = safe_fopen (msg, "r");
  6720. + if (!fp)
  6721. + {
  6722. + mutt_perror (msg);
  6723. + return -1;
  6724. + }
  6725. +
  6726. + strfcpy (buf, "POST\r\n", sizeof (buf));
  6727. + if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
  6728. + return -1;
  6729. + if (buf[0] != '3')
  6730. + {
  6731. + mutt_error (_("Can't post article: %s"), buf);
  6732. + return -1;
  6733. + }
  6734. +
  6735. + buf[0] = '.';
  6736. + buf[1] = '\0';
  6737. + while (fgets (buf + 1, sizeof (buf) - 2, fp))
  6738. + {
  6739. + len = strlen (buf);
  6740. + if (buf[len - 1] == '\n')
  6741. + {
  6742. + buf[len - 1] = '\r';
  6743. + buf[len] = '\n';
  6744. + len++;
  6745. + buf[len] = '\0';
  6746. + }
  6747. + if (mutt_socket_write_d (nntp_data->nserv->conn,
  6748. + buf[1] == '.' ? buf : buf + 1, -1, M_SOCK_LOG_HDR) < 0)
  6749. + return nntp_connect_error (nntp_data->nserv);
  6750. + }
  6751. + fclose (fp);
  6752. +
  6753. + if ((buf[strlen (buf) - 1] != '\n' &&
  6754. + mutt_socket_write_d (nntp_data->nserv->conn, "\r\n", -1, M_SOCK_LOG_HDR) < 0) ||
  6755. + mutt_socket_write_d (nntp_data->nserv->conn, ".\r\n", -1, M_SOCK_LOG_HDR) < 0 ||
  6756. + mutt_socket_readln (buf, sizeof (buf), nntp_data->nserv->conn) < 0)
  6757. + return nntp_connect_error (nntp_data->nserv);
  6758. + if (buf[0] != '2')
  6759. + {
  6760. + mutt_error (_("Can't post article: %s"), buf);
  6761. + return -1;
  6762. + }
  6763. + return 0;
  6764. +}
  6765. +
  6766. +/* Save changes to .newsrc and cache */
  6767. +int nntp_sync_mailbox (CONTEXT *ctx)
  6768. +{
  6769. + NNTP_DATA *nntp_data = ctx->data;
  6770. + int rc, i;
  6771. +#ifdef USE_HCACHE
  6772. + header_cache_t *hc;
  6773. +#endif
  6774. +
  6775. + /* check for new articles */
  6776. + nntp_data->nserv->check_time = 0;
  6777. + rc = nntp_check_mailbox (ctx, 1);
  6778. + if (rc)
  6779. + return rc;
  6780. +
  6781. +#ifdef USE_HCACHE
  6782. + nntp_data->lastCached = 0;
  6783. + hc = nntp_hcache_open (nntp_data);
  6784. +#endif
  6785. +
  6786. + nntp_data->unread = ctx->unread;
  6787. + for (i = 0; i < ctx->msgcount; i++)
  6788. + {
  6789. + HEADER *hdr = ctx->hdrs[i];
  6790. + char buf[16];
  6791. +
  6792. + snprintf (buf, sizeof (buf), "%d", NHDR (hdr)->article_num);
  6793. + if (nntp_data->bcache && hdr->deleted)
  6794. + {
  6795. + dprint (2, (debugfile, "nntp_sync_mailbox: mutt_bcache_del %s\n", buf));
  6796. + mutt_bcache_del (nntp_data->bcache, buf);
  6797. + }
  6798. +
  6799. +#ifdef USE_HCACHE
  6800. + if (hc && (hdr->changed || hdr->deleted))
  6801. + {
  6802. + if (hdr->deleted && !hdr->read)
  6803. + nntp_data->unread--;
  6804. + dprint (2, (debugfile, "nntp_sync_mailbox: mutt_hcache_store %s\n", buf));
  6805. + mutt_hcache_store (hc, buf, hdr, 0, strlen, M_GENERATE_UIDVALIDITY);
  6806. + }
  6807. +#endif
  6808. + }
  6809. +
  6810. +#ifdef USE_HCACHE
  6811. + if (hc)
  6812. + {
  6813. + mutt_hcache_close (hc);
  6814. + nntp_data->lastCached = nntp_data->lastLoaded;
  6815. + }
  6816. +#endif
  6817. +
  6818. + /* save .newsrc entries */
  6819. + nntp_newsrc_gen_entries (ctx);
  6820. + nntp_newsrc_update (nntp_data->nserv);
  6821. + nntp_newsrc_close (nntp_data->nserv);
  6822. + return 0;
  6823. +}
  6824. +
  6825. +/* Free up memory associated with the newsgroup context */
  6826. +int nntp_fastclose_mailbox (CONTEXT *ctx)
  6827. +{
  6828. + NNTP_DATA *nntp_data = ctx->data, *nntp_tmp;
  6829. +
  6830. + if (!nntp_data)
  6831. + return 0;
  6832. +
  6833. + nntp_acache_free (nntp_data);
  6834. + if (!nntp_data->nserv || !nntp_data->nserv->groups_hash || !nntp_data->group)
  6835. + return 0;
  6836. +
  6837. + nntp_tmp = hash_find (nntp_data->nserv->groups_hash, nntp_data->group);
  6838. + if (nntp_tmp == NULL || nntp_tmp != nntp_data)
  6839. + nntp_data_free (nntp_data);
  6840. + return 0;
  6841. +}
  6842. +
  6843. +/* Get date and time from server */
  6844. +int nntp_date (NNTP_SERVER *nserv, time_t *now)
  6845. +{
  6846. + if (nserv->hasDATE)
  6847. + {
  6848. + NNTP_DATA nntp_data;
  6849. + char buf[LONG_STRING];
  6850. + struct tm tm;
  6851. +
  6852. + nntp_data.nserv = nserv;
  6853. + nntp_data.group = NULL;
  6854. + strfcpy (buf, "DATE\r\n", sizeof (buf));
  6855. + if (nntp_query (&nntp_data, buf, sizeof (buf)) < 0)
  6856. + return -1;
  6857. +
  6858. + if (sscanf (buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
  6859. + &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
  6860. + {
  6861. + tm.tm_year -= 1900;
  6862. + tm.tm_mon--;
  6863. + *now = timegm (&tm);
  6864. + if (*now >= 0)
  6865. + {
  6866. + dprint (1, (debugfile, "nntp_date: server time is %d\n", *now));
  6867. + return 0;
  6868. + }
  6869. + }
  6870. + }
  6871. + time (now);
  6872. + return 0;
  6873. +}
  6874. +
  6875. +/* Fetch list of all newsgroups from server */
  6876. +int nntp_active_fetch (NNTP_SERVER *nserv)
  6877. +{
  6878. + NNTP_DATA nntp_data;
  6879. + char msg[SHORT_STRING];
  6880. + char buf[LONG_STRING];
  6881. + unsigned int i;
  6882. + int rc;
  6883. +
  6884. + snprintf (msg, sizeof (msg), _("Loading list of groups from server %s..."),
  6885. + nserv->conn->account.host);
  6886. + mutt_message (msg);
  6887. + if (nntp_date (nserv, &nserv->newgroups_time) < 0)
  6888. + return -1;
  6889. +
  6890. + nntp_data.nserv = nserv;
  6891. + nntp_data.group = NULL;
  6892. + strfcpy (buf, "LIST\r\n", sizeof (buf));
  6893. + rc = nntp_fetch_lines (&nntp_data, buf, sizeof (buf), msg,
  6894. + nntp_add_group, nserv);
  6895. + if (rc)
  6896. + {
  6897. + if (rc > 0)
  6898. + {
  6899. + mutt_error ("LIST: %s", buf);
  6900. + mutt_sleep (2);
  6901. + }
  6902. + return -1;
  6903. + }
  6904. +
  6905. + if (option (OPTLOADDESC) &&
  6906. + get_description (&nntp_data, "*", _("Loading descriptions...")) < 0)
  6907. + return -1;
  6908. +
  6909. + for (i = 0; i < nserv->groups_num; i++)
  6910. + {
  6911. + NNTP_DATA *nntp_data = nserv->groups_list[i];
  6912. +
  6913. + if (nntp_data && nntp_data->deleted && !nntp_data->newsrc_ent)
  6914. + {
  6915. + nntp_delete_group_cache (nntp_data);
  6916. + hash_delete (nserv->groups_hash, nntp_data->group, NULL, nntp_data_free);
  6917. + nserv->groups_list[i] = NULL;
  6918. + }
  6919. + }
  6920. + nntp_active_save_cache (nserv);
  6921. + mutt_clear_error ();
  6922. + return 0;
  6923. +}
  6924. +
  6925. +/* Check newsgroup for new articles:
  6926. + * 1 - new articles found
  6927. + * 0 - no change
  6928. + * -1 - lost connection */
  6929. +static int nntp_group_poll (NNTP_DATA *nntp_data, int update_stat)
  6930. +{
  6931. + char buf[LONG_STRING] = "";
  6932. + anum_t count, first, last;
  6933. +
  6934. + /* use GROUP command to poll newsgroup */
  6935. + if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
  6936. + return -1;
  6937. + if (sscanf (buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
  6938. + return 0;
  6939. + if (first == nntp_data->firstMessage && last == nntp_data->lastMessage)
  6940. + return 0;
  6941. +
  6942. + /* articles have been renumbered */
  6943. + if (last < nntp_data->lastMessage)
  6944. + {
  6945. + nntp_data->lastCached = 0;
  6946. + if (nntp_data->newsrc_len)
  6947. + {
  6948. + safe_realloc (&nntp_data->newsrc_ent, sizeof (NEWSRC_ENTRY));
  6949. + nntp_data->newsrc_len = 1;
  6950. + nntp_data->newsrc_ent[0].first = 1;
  6951. + nntp_data->newsrc_ent[0].last = 0;
  6952. + }
  6953. + }
  6954. + nntp_data->firstMessage = first;
  6955. + nntp_data->lastMessage = last;
  6956. + if (!update_stat)
  6957. + return 1;
  6958. +
  6959. + /* update counters */
  6960. + else if (!last || (!nntp_data->newsrc_ent && !nntp_data->lastCached))
  6961. + nntp_data->unread = count;
  6962. + else
  6963. + nntp_group_unread_stat (nntp_data);
  6964. + return 1;
  6965. +}
  6966. +
  6967. +/* Check current newsgroup for new articles:
  6968. + * M_REOPENED - articles have been renumbered or removed from server
  6969. + * M_NEW_MAIL - new articles found
  6970. + * 0 - no change
  6971. + * -1 - lost connection */
  6972. +int nntp_check_mailbox (CONTEXT *ctx, int leave_lock)
  6973. +{
  6974. + NNTP_DATA *nntp_data = ctx->data;
  6975. + NNTP_SERVER *nserv = nntp_data->nserv;
  6976. + time_t now = time (NULL);
  6977. + int i, j;
  6978. + int rc, ret = 0;
  6979. + void *hc = NULL;
  6980. +
  6981. + if (nserv->check_time + NewsPollTimeout > now)
  6982. + return 0;
  6983. +
  6984. + mutt_message _("Checking for new messages...");
  6985. + if (nntp_newsrc_parse (nserv) < 0)
  6986. + return -1;
  6987. +
  6988. + nserv->check_time = now;
  6989. + rc = nntp_group_poll (nntp_data, 0);
  6990. + if (rc < 0)
  6991. + {
  6992. + nntp_newsrc_close (nserv);
  6993. + return -1;
  6994. + }
  6995. + if (rc)
  6996. + nntp_active_save_cache (nserv);
  6997. +
  6998. + /* articles have been renumbered, remove all headers */
  6999. + if (nntp_data->lastMessage < nntp_data->lastLoaded)
  7000. + {
  7001. + for (i = 0; i < ctx->msgcount; i++)
  7002. + mutt_free_header (&ctx->hdrs[i]);
  7003. + ctx->msgcount = 0;
  7004. + ctx->tagged = 0;
  7005. +
  7006. + if (nntp_data->lastMessage < nntp_data->lastLoaded)
  7007. + {
  7008. + nntp_data->lastLoaded = nntp_data->firstMessage - 1;
  7009. + if (NntpContext && nntp_data->lastMessage - nntp_data->lastLoaded >
  7010. + NntpContext)
  7011. + nntp_data->lastLoaded = nntp_data->lastMessage - NntpContext;
  7012. + }
  7013. + ret = M_REOPENED;
  7014. + }
  7015. +
  7016. + /* .newsrc has been externally modified */
  7017. + if (nserv->newsrc_modified)
  7018. + {
  7019. + anum_t anum;
  7020. +#ifdef USE_HCACHE
  7021. + unsigned char *messages;
  7022. + char buf[16];
  7023. + void *hdata;
  7024. + HEADER *hdr;
  7025. + anum_t first = nntp_data->firstMessage;
  7026. +
  7027. + if (NntpContext && nntp_data->lastMessage - first + 1 > NntpContext)
  7028. + first = nntp_data->lastMessage - NntpContext + 1;
  7029. + messages = safe_calloc (nntp_data->lastLoaded - first + 1,
  7030. + sizeof (unsigned char));
  7031. + hc = nntp_hcache_open (nntp_data);
  7032. + nntp_hcache_update (nntp_data, hc);
  7033. +#endif
  7034. +
  7035. + /* update flags according to .newsrc */
  7036. + for (i = j = 0; i < ctx->msgcount; i++)
  7037. + {
  7038. + int flagged = 0;
  7039. + anum = NHDR (ctx->hdrs[i])->article_num;
  7040. +
  7041. +#ifdef USE_HCACHE
  7042. + /* check hcache for flagged and deleted flags */
  7043. + if (hc)
  7044. + {
  7045. + if (anum >= first && anum <= nntp_data->lastLoaded)
  7046. + messages[anum - first] = 1;
  7047. +
  7048. + snprintf (buf, sizeof (buf), "%d", anum);
  7049. + hdata = mutt_hcache_fetch (hc, buf, strlen);
  7050. + if (hdata)
  7051. + {
  7052. + int deleted;
  7053. +
  7054. + dprint (2, (debugfile,
  7055. + "nntp_check_mailbox: mutt_hcache_fetch %s\n", buf));
  7056. + hdr = mutt_hcache_restore (hdata, NULL);
  7057. + deleted = hdr->deleted;
  7058. + flagged = hdr->flagged;
  7059. + mutt_free_header (&hdr);
  7060. +
  7061. + /* header marked as deleted, removing from context */
  7062. + if (deleted)
  7063. + {
  7064. + mutt_set_flag (ctx, ctx->hdrs[i], M_TAG, 0);
  7065. + mutt_free_header (&ctx->hdrs[i]);
  7066. + continue;
  7067. + }
  7068. + }
  7069. + }
  7070. +#endif
  7071. +
  7072. + if (!ctx->hdrs[i]->changed)
  7073. + {
  7074. + ctx->hdrs[i]->flagged = flagged;
  7075. + ctx->hdrs[i]->read = 0;
  7076. + ctx->hdrs[i]->old = 0;
  7077. + nntp_article_status (ctx, ctx->hdrs[i], NULL, anum);
  7078. + if (!ctx->hdrs[i]->read)
  7079. + nntp_parse_xref (ctx, ctx->hdrs[i]);
  7080. + }
  7081. + ctx->hdrs[j++] = ctx->hdrs[i];
  7082. + }
  7083. +
  7084. +#ifdef USE_HCACHE
  7085. + ctx->msgcount = j;
  7086. +
  7087. + /* restore headers without "deleted" flag */
  7088. + for (anum = first; anum <= nntp_data->lastLoaded; anum++)
  7089. + {
  7090. + if (messages[anum - first])
  7091. + continue;
  7092. +
  7093. + snprintf (buf, sizeof (buf), "%d", anum);
  7094. + hdata = mutt_hcache_fetch (hc, buf, strlen);
  7095. + if (hdata)
  7096. + {
  7097. + dprint (2, (debugfile,
  7098. + "nntp_check_mailbox: mutt_hcache_fetch %s\n", buf));
  7099. + if (ctx->msgcount >= ctx->hdrmax)
  7100. + mx_alloc_memory (ctx);
  7101. +
  7102. + ctx->hdrs[ctx->msgcount] =
  7103. + hdr = mutt_hcache_restore (hdata, NULL);
  7104. + if (hdr->deleted)
  7105. + {
  7106. + mutt_free_header (&hdr);
  7107. + if (nntp_data->bcache)
  7108. + {
  7109. + dprint (2, (debugfile,
  7110. + "nntp_check_mailbox: mutt_bcache_del %s\n", buf));
  7111. + mutt_bcache_del (nntp_data->bcache, buf);
  7112. + }
  7113. + continue;
  7114. + }
  7115. +
  7116. + ctx->msgcount++;
  7117. + hdr->read = 0;
  7118. + hdr->old = 0;
  7119. + hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
  7120. + NHDR (hdr)->article_num = anum;
  7121. + nntp_article_status (ctx, hdr, NULL, anum);
  7122. + if (!hdr->read)
  7123. + nntp_parse_xref (ctx, hdr);
  7124. + }
  7125. + }
  7126. + FREE (&messages);
  7127. +#endif
  7128. +
  7129. + nserv->newsrc_modified = 0;
  7130. + ret = M_REOPENED;
  7131. + }
  7132. +
  7133. + /* some headers were removed, context must be updated */
  7134. + if (ret == M_REOPENED)
  7135. + {
  7136. + if (ctx->subj_hash)
  7137. + hash_destroy (&ctx->subj_hash, NULL);
  7138. + if (ctx->id_hash)
  7139. + hash_destroy (&ctx->id_hash, NULL);
  7140. + mutt_clear_threads (ctx);
  7141. +
  7142. + ctx->vcount = 0;
  7143. + ctx->deleted = 0;
  7144. + ctx->new = 0;
  7145. + ctx->unread = 0;
  7146. + ctx->flagged = 0;
  7147. + ctx->changed = 0;
  7148. + ctx->id_hash = NULL;
  7149. + ctx->subj_hash = NULL;
  7150. + mx_update_context (ctx, ctx->msgcount);
  7151. + }
  7152. +
  7153. + /* fetch headers of new articles */
  7154. + if (nntp_data->lastMessage > nntp_data->lastLoaded)
  7155. + {
  7156. + int oldmsgcount = ctx->msgcount;
  7157. + int quiet = ctx->quiet;
  7158. + ctx->quiet = 1;
  7159. +#ifdef USE_HCACHE
  7160. + if (!hc)
  7161. + {
  7162. + hc = nntp_hcache_open (nntp_data);
  7163. + nntp_hcache_update (nntp_data, hc);
  7164. + }
  7165. +#endif
  7166. + rc = nntp_fetch_headers (ctx, hc, nntp_data->lastLoaded + 1,
  7167. + nntp_data->lastMessage, 0);
  7168. + ctx->quiet = quiet;
  7169. + if (rc >= 0)
  7170. + nntp_data->lastLoaded = nntp_data->lastMessage;
  7171. + if (ret == 0 && ctx->msgcount > oldmsgcount)
  7172. + ret = M_NEW_MAIL;
  7173. + }
  7174. +
  7175. +#ifdef USE_HCACHE
  7176. + mutt_hcache_close (hc);
  7177. +#endif
  7178. + if (ret || !leave_lock)
  7179. + nntp_newsrc_close (nserv);
  7180. + mutt_clear_error ();
  7181. + return ret;
  7182. +}
  7183. +
  7184. +/* Check for new groups and new articles in subscribed groups:
  7185. + * 1 - new groups found
  7186. + * 0 - no new groups
  7187. + * -1 - error */
  7188. +int nntp_check_new_groups (NNTP_SERVER *nserv)
  7189. +{
  7190. + NNTP_DATA nntp_data;
  7191. + time_t now;
  7192. + struct tm *tm;
  7193. + char buf[LONG_STRING];
  7194. + char *msg = _("Checking for new newsgroups...");
  7195. + unsigned int i;
  7196. + int rc, update_active = FALSE;
  7197. +
  7198. + if (!nserv || !nserv->newgroups_time)
  7199. + return -1;
  7200. +
  7201. + /* check subscribed newsgroups for new articles */
  7202. + if (option (OPTSHOWNEWNEWS))
  7203. + {
  7204. + mutt_message _("Checking for new messages...");
  7205. + for (i = 0; i < nserv->groups_num; i++)
  7206. + {
  7207. + NNTP_DATA *nntp_data = nserv->groups_list[i];
  7208. +
  7209. + if (nntp_data && nntp_data->subscribed)
  7210. + {
  7211. + rc = nntp_group_poll (nntp_data, 1);
  7212. + if (rc < 0)
  7213. + return -1;
  7214. + if (rc > 0)
  7215. + update_active = TRUE;
  7216. + }
  7217. + }
  7218. + }
  7219. + else if (nserv->newgroups_time)
  7220. + return 0;
  7221. +
  7222. + /* get list of new groups */
  7223. + mutt_message (msg);
  7224. + if (nntp_date (nserv, &now) < 0)
  7225. + return -1;
  7226. + nntp_data.nserv = nserv;
  7227. + if (Context && Context->magic == M_NNTP)
  7228. + nntp_data.group = ((NNTP_DATA *)Context->data)->group;
  7229. + else
  7230. + nntp_data.group = NULL;
  7231. + i = nserv->groups_num;
  7232. + tm = gmtime (&nserv->newgroups_time);
  7233. + snprintf (buf, sizeof (buf), "NEWGROUPS %02d%02d%02d %02d%02d%02d GMT\r\n",
  7234. + tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
  7235. + tm->tm_hour, tm->tm_min, tm->tm_sec);
  7236. + rc = nntp_fetch_lines (&nntp_data, buf, sizeof (buf), msg,
  7237. + nntp_add_group, nserv);
  7238. + if (rc)
  7239. + {
  7240. + if (rc > 0)
  7241. + {
  7242. + mutt_error ("NEWGROUPS: %s", buf);
  7243. + mutt_sleep (2);
  7244. + }
  7245. + return -1;
  7246. + }
  7247. +
  7248. + /* new groups found */
  7249. + rc = 0;
  7250. + if (nserv->groups_num != i)
  7251. + {
  7252. + nserv->newgroups_time = now;
  7253. +
  7254. + /* loading descriptions */
  7255. + if (option (OPTLOADDESC))
  7256. + {
  7257. + unsigned int count = 0;
  7258. + progress_t progress;
  7259. +
  7260. + mutt_progress_init (&progress, _("Loading descriptions..."),
  7261. + M_PROGRESS_MSG, ReadInc, nserv->groups_num - i);
  7262. + for (; i < nserv->groups_num; i++)
  7263. + {
  7264. + NNTP_DATA *nntp_data = nserv->groups_list[i];
  7265. +
  7266. + if (get_description (nntp_data, NULL, NULL) < 0)
  7267. + return -1;
  7268. + mutt_progress_update (&progress, ++count, -1);
  7269. + }
  7270. + }
  7271. + update_active = TRUE;
  7272. + rc = 1;
  7273. + }
  7274. + if (update_active)
  7275. + nntp_active_save_cache (nserv);
  7276. + mutt_clear_error ();
  7277. + return rc;
  7278. +}
  7279. +
  7280. +/* Fetch article by Message-ID:
  7281. + * 0 - success
  7282. + * 1 - no such article
  7283. + * -1 - error */
  7284. +int nntp_check_msgid (CONTEXT *ctx, const char *msgid)
  7285. +{
  7286. + NNTP_DATA *nntp_data = ctx->data;
  7287. + HEADER *hdr;
  7288. + FILE *fp;
  7289. + char tempfile[_POSIX_PATH_MAX];
  7290. + char buf[LONG_STRING];
  7291. + int rc;
  7292. +
  7293. + mutt_mktemp (tempfile, sizeof (tempfile));
  7294. + fp = safe_fopen (tempfile, "w+");
  7295. + if (!fp)
  7296. + {
  7297. + mutt_perror (tempfile);
  7298. + unlink (tempfile);
  7299. + return -1;
  7300. + }
  7301. +
  7302. + snprintf (buf, sizeof (buf), "HEAD %s\r\n", msgid);
  7303. + rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
  7304. + fetch_tempfile, fp);
  7305. + if (rc)
  7306. + {
  7307. + fclose (fp);
  7308. + unlink (tempfile);
  7309. + if (rc < 0)
  7310. + return -1;
  7311. + if (!mutt_strncmp ("430", buf, 3))
  7312. + return 1;
  7313. + mutt_error ("HEAD: %s", buf);
  7314. + return -1;
  7315. + }
  7316. +
  7317. + /* parse header */
  7318. + if (ctx->msgcount == ctx->hdrmax)
  7319. + mx_alloc_memory (ctx);
  7320. + hdr = ctx->hdrs[ctx->msgcount] = mutt_new_header ();
  7321. + hdr->data = safe_calloc (1, sizeof (NNTP_HEADER_DATA));
  7322. + hdr->env = mutt_read_rfc822_header (fp, hdr, 0, 0);
  7323. + fclose (fp);
  7324. + unlink (tempfile);
  7325. +
  7326. + /* get article number */
  7327. + if (hdr->env->xref)
  7328. + nntp_parse_xref (ctx, hdr);
  7329. + else
  7330. + {
  7331. + snprintf (buf, sizeof (buf), "STAT %s\r\n", msgid);
  7332. + if (nntp_query (nntp_data, buf, sizeof (buf)) < 0)
  7333. + {
  7334. + mutt_free_header (&hdr);
  7335. + return -1;
  7336. + }
  7337. + sscanf (buf + 4, ANUM, &NHDR (hdr)->article_num);
  7338. + }
  7339. +
  7340. + /* reset flags */
  7341. + hdr->read = 0;
  7342. + hdr->old = 0;
  7343. + hdr->deleted = 0;
  7344. + hdr->changed = 1;
  7345. + hdr->received = hdr->date_sent;
  7346. + hdr->index = ctx->msgcount++;
  7347. + mx_update_context (ctx, 1);
  7348. + return 0;
  7349. +}
  7350. +
  7351. +typedef struct
  7352. +{
  7353. + CONTEXT *ctx;
  7354. + unsigned int num;
  7355. + unsigned int max;
  7356. + anum_t *child;
  7357. +} CHILD_CTX;
  7358. +
  7359. +/* Parse XPAT line */
  7360. +static int fetch_children (char *line, void *data)
  7361. +{
  7362. + CHILD_CTX *cc = data;
  7363. + anum_t anum;
  7364. + unsigned int i;
  7365. +
  7366. + if (!line || sscanf (line, ANUM, &anum) != 1)
  7367. + return 0;
  7368. + for (i = 0; i < cc->ctx->msgcount; i++)
  7369. + if (NHDR (cc->ctx->hdrs[i])->article_num == anum)
  7370. + return 0;
  7371. + if (cc->num >= cc->max)
  7372. + {
  7373. + cc->max *= 2;
  7374. + safe_realloc (&cc->child, sizeof (anum_t) * cc->max);
  7375. + }
  7376. + cc->child[cc->num++] = anum;
  7377. + return 0;
  7378. +}
  7379. +
  7380. +/* Fetch children of article with the Message-ID */
  7381. +int nntp_check_children (CONTEXT *ctx, const char *msgid)
  7382. +{
  7383. + NNTP_DATA *nntp_data = ctx->data;
  7384. + CHILD_CTX cc;
  7385. + char buf[STRING];
  7386. + int i, rc, quiet;
  7387. + void *hc = NULL;
  7388. +
  7389. + if (!nntp_data || !nntp_data->nserv)
  7390. + return -1;
  7391. + if (nntp_data->firstMessage > nntp_data->lastLoaded)
  7392. + return 0;
  7393. +
  7394. + /* init context */
  7395. + cc.ctx = ctx;
  7396. + cc.num = 0;
  7397. + cc.max = 10;
  7398. + cc.child = safe_malloc (sizeof (anum_t) * cc.max);
  7399. +
  7400. + /* fetch numbers of child messages */
  7401. + snprintf (buf, sizeof (buf), "XPAT References %d-%d *%s*\r\n",
  7402. + nntp_data->firstMessage, nntp_data->lastLoaded, msgid);
  7403. + rc = nntp_fetch_lines (nntp_data, buf, sizeof (buf), NULL,
  7404. + fetch_children, &cc);
  7405. + if (rc)
  7406. + {
  7407. + FREE (&cc.child);
  7408. + if (rc > 0) {
  7409. + if (mutt_strncmp ("500", buf, 3))
  7410. + mutt_error ("XPAT: %s", buf);
  7411. + else
  7412. + mutt_error _("Unable to find child articles because server does not support XPAT command.");
  7413. + }
  7414. + return -1;
  7415. + }
  7416. +
  7417. + /* fetch all found messages */
  7418. + quiet = ctx->quiet;
  7419. + ctx->quiet = 1;
  7420. +#ifdef USE_HCACHE
  7421. + hc = nntp_hcache_open (nntp_data);
  7422. +#endif
  7423. + for (i = 0; i < cc.num; i++)
  7424. + {
  7425. + rc = nntp_fetch_headers (ctx, hc, cc.child[i], cc.child[i], 1);
  7426. + if (rc < 0)
  7427. + break;
  7428. + }
  7429. +#ifdef USE_HCACHE
  7430. + mutt_hcache_close (hc);
  7431. +#endif
  7432. + ctx->quiet = quiet;
  7433. + FREE (&cc.child);
  7434. + return rc < 0 ? -1 : 0;
  7435. +}
  7436. diff --git a/nntp.h b/nntp.h
  7437. new file mode 100644
  7438. index 0000000..c937034
  7439. --- /dev/null
  7440. +++ b/nntp.h
  7441. @@ -0,0 +1,167 @@
  7442. +/*
  7443. + * Copyright (C) 1998 Brandon Long <blong@fiction.net>
  7444. + * Copyright (C) 1999 Andrej Gritsenko <andrej@lucky.net>
  7445. + * Copyright (C) 2000-2012 Vsevolod Volkov <vvv@mutt.org.ua>
  7446. + *
  7447. + * This program is free software; you can redistribute it and/or modify
  7448. + * it under the terms of the GNU General Public License as published by
  7449. + * the Free Software Foundation; either version 2 of the License, or
  7450. + * (at your option) any later version.
  7451. + *
  7452. + * This program is distributed in the hope that it will be useful,
  7453. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  7454. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  7455. + * GNU General Public License for more details.
  7456. + *
  7457. + * You should have received a copy of the GNU General Public License
  7458. + * along with this program; if not, write to the Free Software
  7459. + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  7460. + */
  7461. +
  7462. +#ifndef _NNTP_H_
  7463. +#define _NNTP_H_ 1
  7464. +
  7465. +#include "mutt_socket.h"
  7466. +#include "mailbox.h"
  7467. +#include "bcache.h"
  7468. +
  7469. +#if USE_HCACHE
  7470. +#include "hcache.h"
  7471. +#endif
  7472. +
  7473. +#include <time.h>
  7474. +#include <sys/types.h>
  7475. +#include <stdint.h>
  7476. +
  7477. +#define NNTP_PORT 119
  7478. +#define NNTP_SSL_PORT 563
  7479. +
  7480. +/* number of entries in article cache */
  7481. +#define NNTP_ACACHE_LEN 10
  7482. +
  7483. +/* article number type and format */
  7484. +#define anum_t uint32_t
  7485. +#define ANUM "%u"
  7486. +
  7487. +enum
  7488. +{
  7489. + NNTP_NONE = 0,
  7490. + NNTP_OK,
  7491. + NNTP_BYE
  7492. +};
  7493. +
  7494. +typedef struct
  7495. +{
  7496. + unsigned int hasCAPABILITIES : 1;
  7497. + unsigned int hasSTARTTLS : 1;
  7498. + unsigned int hasDATE : 1;
  7499. + unsigned int hasLIST_NEWSGROUPS : 1;
  7500. + unsigned int hasXGTITLE : 1;
  7501. + unsigned int hasLISTGROUP : 1;
  7502. + unsigned int hasOVER : 1;
  7503. + unsigned int hasXOVER : 1;
  7504. + unsigned int use_tls : 3;
  7505. + unsigned int status : 3;
  7506. + unsigned int cacheable : 1;
  7507. + unsigned int newsrc_modified : 1;
  7508. + FILE *newsrc_fp;
  7509. + char *newsrc_file;
  7510. + char *authenticators;
  7511. + char *overview_fmt;
  7512. + off_t size;
  7513. + time_t mtime;
  7514. + time_t newgroups_time;
  7515. + time_t check_time;
  7516. + unsigned int groups_num;
  7517. + unsigned int groups_max;
  7518. + void **groups_list;
  7519. + HASH *groups_hash;
  7520. + CONNECTION *conn;
  7521. +} NNTP_SERVER;
  7522. +
  7523. +typedef struct
  7524. +{
  7525. + anum_t first;
  7526. + anum_t last;
  7527. +} NEWSRC_ENTRY;
  7528. +
  7529. +typedef struct
  7530. +{
  7531. + unsigned int index;
  7532. + char *path;
  7533. +} NNTP_ACACHE;
  7534. +
  7535. +typedef struct
  7536. +{
  7537. + char *group;
  7538. + char *desc;
  7539. + anum_t firstMessage;
  7540. + anum_t lastMessage;
  7541. + anum_t lastLoaded;
  7542. + anum_t lastCached;
  7543. + anum_t unread;
  7544. + unsigned int subscribed : 1;
  7545. + unsigned int new : 1;
  7546. + unsigned int allowed : 1;
  7547. + unsigned int deleted : 1;
  7548. + unsigned int newsrc_len;
  7549. + NEWSRC_ENTRY *newsrc_ent;
  7550. + NNTP_SERVER *nserv;
  7551. + NNTP_ACACHE acache[NNTP_ACACHE_LEN];
  7552. + body_cache_t *bcache;
  7553. +} NNTP_DATA;
  7554. +
  7555. +typedef struct
  7556. +{
  7557. + anum_t article_num;
  7558. + unsigned int parsed : 1;
  7559. +} NNTP_HEADER_DATA;
  7560. +
  7561. +#define NHDR(hdr) ((NNTP_HEADER_DATA*)((hdr)->data))
  7562. +
  7563. +/* internal functions */
  7564. +int nntp_add_group (char *, void *);
  7565. +int nntp_active_save_cache (NNTP_SERVER *);
  7566. +int nntp_check_new_groups (NNTP_SERVER *);
  7567. +int nntp_fastclose_mailbox (CONTEXT *);
  7568. +int nntp_open_connection (NNTP_SERVER *);
  7569. +void nntp_newsrc_gen_entries (CONTEXT *);
  7570. +void nntp_bcache_update (NNTP_DATA *);
  7571. +void nntp_article_status (CONTEXT *, HEADER *, char *, anum_t);
  7572. +void nntp_group_unread_stat (NNTP_DATA *);
  7573. +void nntp_data_free (void *);
  7574. +void nntp_acache_free (NNTP_DATA *);
  7575. +void nntp_delete_group_cache (NNTP_DATA *);
  7576. +
  7577. +/* exposed interface */
  7578. +NNTP_SERVER *nntp_select_server (char *, int);
  7579. +NNTP_DATA *mutt_newsgroup_subscribe (NNTP_SERVER *, char *);
  7580. +NNTP_DATA *mutt_newsgroup_unsubscribe (NNTP_SERVER *, char *);
  7581. +NNTP_DATA *mutt_newsgroup_catchup (NNTP_SERVER *, char *);
  7582. +NNTP_DATA *mutt_newsgroup_uncatchup (NNTP_SERVER *, char *);
  7583. +int nntp_active_fetch (NNTP_SERVER *);
  7584. +int nntp_newsrc_update (NNTP_SERVER *);
  7585. +int nntp_open_mailbox (CONTEXT *);
  7586. +int nntp_sync_mailbox (CONTEXT *);
  7587. +int nntp_check_mailbox (CONTEXT *, int);
  7588. +int nntp_fetch_message (MESSAGE *, CONTEXT *, int);
  7589. +int nntp_post (const char *);
  7590. +int nntp_check_msgid (CONTEXT *, const char *);
  7591. +int nntp_check_children (CONTEXT *, const char *);
  7592. +int nntp_newsrc_parse (NNTP_SERVER *);
  7593. +void nntp_newsrc_close (NNTP_SERVER *);
  7594. +void nntp_buffy (char *, size_t);
  7595. +void nntp_expand_path (char *, size_t, ACCOUNT *);
  7596. +void nntp_clear_cache (NNTP_SERVER *);
  7597. +const char *nntp_format_str (char *, size_t, size_t, char, const char *,
  7598. + const char *, const char *, const char *,
  7599. + unsigned long, format_flag);
  7600. +
  7601. +NNTP_SERVER *CurrentNewsSrv INITVAL (NULL);
  7602. +
  7603. +#ifdef USE_HCACHE
  7604. +header_cache_t *nntp_hcache_open (NNTP_DATA *);
  7605. +void nntp_hcache_update (NNTP_DATA *, header_cache_t *);
  7606. +#endif
  7607. +
  7608. +#endif /* _NNTP_H_ */
  7609. diff --git a/pager.c b/pager.c
  7610. index d372fc0..e888452 100644
  7611. --- a/pager.c
  7612. +++ b/pager.c
  7613. @@ -1085,6 +1085,11 @@ fill_buffer (FILE *f, LOFF_T *last_pos, LOFF_T offset, unsigned char **buf,
  7614. return b_read;
  7615. }
  7616. +#ifdef USE_NNTP
  7617. +#include "mx.h"
  7618. +#include "nntp.h"
  7619. +#endif
  7620. +
  7621. static int format_line (struct line_t **lineInfo, int n, unsigned char *buf,
  7622. int flags, ansi_attr *pa, int cnt,
  7623. @@ -1543,6 +1548,16 @@ static const struct mapping_t PagerHelpExtra[] = {
  7624. { NULL, 0 }
  7625. };
  7626. +#ifdef USE_NNTP
  7627. +static struct mapping_t PagerNewsHelpExtra[] = {
  7628. + { N_("Post"), OP_POST },
  7629. + { N_("Followup"), OP_FOLLOWUP },
  7630. + { N_("Del"), OP_DELETE },
  7631. + { N_("Next"), OP_MAIN_NEXT_UNDELETED },
  7632. + { NULL, 0 }
  7633. +};
  7634. +#endif
  7635. +
  7636. /* This pager is actually not so simple as it once was. It now operates in
  7637. @@ -1584,6 +1599,10 @@ mutt_pager (const char *banner, const char *fname, int flags, pager_t *extra)
  7638. int old_PagerIndexLines; /* some people want to resize it
  7639. * while inside the pager... */
  7640. +#ifdef USE_NNTP
  7641. + char *followup_to;
  7642. +#endif
  7643. +
  7644. if (!(flags & M_SHOWCOLOR))
  7645. flags |= M_SHOWFLAT;
  7646. @@ -1623,7 +1642,11 @@ mutt_pager (const char *banner, const char *fname, int flags, pager_t *extra)
  7647. if (IsHeader (extra))
  7648. {
  7649. strfcpy (tmphelp, helpstr, sizeof (tmphelp));
  7650. - mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER, PagerHelpExtra);
  7651. + mutt_compile_help (buffer, sizeof (buffer), MENU_PAGER,
  7652. +#ifdef USE_NNTP
  7653. + (Context && (Context->magic == M_NNTP)) ? PagerNewsHelpExtra :
  7654. +#endif
  7655. + PagerHelpExtra);
  7656. snprintf (helpstr, sizeof (helpstr), "%s %s", tmphelp, buffer);
  7657. }
  7658. if (!InHelp)
  7659. @@ -2551,6 +2574,60 @@ search_next:
  7660. redraw = REDRAW_FULL;
  7661. break;
  7662. +#ifdef USE_NNTP
  7663. + case OP_POST:
  7664. + CHECK_MODE(IsHeader (extra) && !IsAttach (extra));
  7665. + CHECK_ATTACH;
  7666. + if (extra->ctx && extra->ctx->magic == M_NNTP &&
  7667. + !((NNTP_DATA *)extra->ctx->data)->allowed &&
  7668. + query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != M_YES)
  7669. + break;
  7670. + ci_send_message (SENDNEWS, NULL, NULL, extra->ctx, NULL);
  7671. + redraw = REDRAW_FULL;
  7672. + break;
  7673. +
  7674. + case OP_FORWARD_TO_GROUP:
  7675. + CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra));
  7676. + CHECK_ATTACH;
  7677. + if (extra->ctx && extra->ctx->magic == M_NNTP &&
  7678. + !((NNTP_DATA *)extra->ctx->data)->allowed &&
  7679. + query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != M_YES)
  7680. + break;
  7681. + if (IsMsgAttach (extra))
  7682. + mutt_attach_forward (extra->fp, extra->hdr, extra->idx,
  7683. + extra->idxlen, extra->bdy, SENDNEWS);
  7684. + else
  7685. + ci_send_message (SENDNEWS|SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr);
  7686. + redraw = REDRAW_FULL;
  7687. + break;
  7688. +
  7689. + case OP_FOLLOWUP:
  7690. + CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra));
  7691. + CHECK_ATTACH;
  7692. +
  7693. + if (IsMsgAttach (extra))
  7694. + followup_to = extra->bdy->hdr->env->followup_to;
  7695. + else
  7696. + followup_to = extra->hdr->env->followup_to;
  7697. +
  7698. + if (!followup_to || mutt_strcasecmp (followup_to, "poster") ||
  7699. + query_quadoption (OPT_FOLLOWUPTOPOSTER,_("Reply by mail as poster prefers?")) != M_YES)
  7700. + {
  7701. + if (extra->ctx && extra->ctx->magic == M_NNTP &&
  7702. + !((NNTP_DATA *)extra->ctx->data)->allowed &&
  7703. + query_quadoption (OPT_TOMODERATED,_("Posting to this group not allowed, may be moderated. Continue?")) != M_YES)
  7704. + break;
  7705. + if (IsMsgAttach (extra))
  7706. + mutt_attach_reply (extra->fp, extra->hdr, extra->idx,
  7707. + extra->idxlen, extra->bdy, SENDNEWS|SENDREPLY);
  7708. + else
  7709. + ci_send_message (SENDNEWS|SENDREPLY, NULL, NULL,
  7710. + extra->ctx, extra->hdr);
  7711. + redraw = REDRAW_FULL;
  7712. + break;
  7713. + }
  7714. +#endif
  7715. +
  7716. case OP_REPLY:
  7717. CHECK_MODE(IsHeader (extra) || IsMsgAttach (extra));
  7718. CHECK_ATTACH;
  7719. @@ -2597,7 +2674,7 @@ search_next:
  7720. CHECK_ATTACH;
  7721. if (IsMsgAttach (extra))
  7722. mutt_attach_forward (extra->fp, extra->hdr, extra->idx,
  7723. - extra->idxlen, extra->bdy);
  7724. + extra->idxlen, extra->bdy, 0);
  7725. else
  7726. ci_send_message (SENDFORWARD, NULL, NULL, extra->ctx, extra->hdr);
  7727. redraw = REDRAW_FULL;
  7728. diff --git a/parse.c b/parse.c
  7729. index 58c7774..36046e9 100644
  7730. --- a/parse.c
  7731. +++ b/parse.c
  7732. @@ -89,7 +89,7 @@ char *mutt_read_rfc822_line (FILE *f, char *line, size_t *linelen)
  7733. /* not reached */
  7734. }
  7735. -static LIST *mutt_parse_references (char *s, int in_reply_to)
  7736. +LIST *mutt_parse_references (char *s, int in_reply_to)
  7737. {
  7738. LIST *t, *lst = NULL;
  7739. char *m;
  7740. @@ -1072,6 +1072,17 @@ int mutt_parse_rfc822_line (ENVELOPE *e, HEADER *hdr, char *line, char *p, short
  7741. e->from = rfc822_parse_adrlist (e->from, p);
  7742. matched = 1;
  7743. }
  7744. +#ifdef USE_NNTP
  7745. + else if (!mutt_strcasecmp (line+1, "ollowup-to"))
  7746. + {
  7747. + if (!e->followup_to)
  7748. + {
  7749. + mutt_remove_trailing_ws (p);
  7750. + e->followup_to = safe_strdup (mutt_skip_whitespace (p));
  7751. + }
  7752. + matched = 1;
  7753. + }
  7754. +#endif
  7755. break;
  7756. case 'i':
  7757. @@ -1154,6 +1165,27 @@ int mutt_parse_rfc822_line (ENVELOPE *e, HEADER *hdr, char *line, char *p, short
  7758. }
  7759. break;
  7760. +#ifdef USE_NNTP
  7761. + case 'n':
  7762. + if (!mutt_strcasecmp (line + 1, "ewsgroups"))
  7763. + {
  7764. + FREE (&e->newsgroups);
  7765. + mutt_remove_trailing_ws (p);
  7766. + e->newsgroups = safe_strdup (mutt_skip_whitespace (p));
  7767. + matched = 1;
  7768. + }
  7769. + break;
  7770. +#endif
  7771. +
  7772. + case 'o':
  7773. + /* field `Organization:' saves only for pager! */
  7774. + if (!mutt_strcasecmp (line + 1, "rganization"))
  7775. + {
  7776. + if (!e->organization && mutt_strcasecmp (p, "unknown"))
  7777. + e->organization = safe_strdup (p);
  7778. + }
  7779. + break;
  7780. +
  7781. case 'r':
  7782. if (!ascii_strcasecmp (line + 1, "eferences"))
  7783. {
  7784. @@ -1266,6 +1298,20 @@ int mutt_parse_rfc822_line (ENVELOPE *e, HEADER *hdr, char *line, char *p, short
  7785. e->x_label = safe_strdup(p);
  7786. matched = 1;
  7787. }
  7788. +#ifdef USE_NNTP
  7789. + else if (!mutt_strcasecmp (line + 1, "-comment-to"))
  7790. + {
  7791. + if (!e->x_comment_to)
  7792. + e->x_comment_to = safe_strdup (p);
  7793. + matched = 1;
  7794. + }
  7795. + else if (!mutt_strcasecmp (line + 1, "ref"))
  7796. + {
  7797. + if (!e->xref)
  7798. + e->xref = safe_strdup (p);
  7799. + matched = 1;
  7800. + }
  7801. +#endif
  7802. default:
  7803. break;
  7804. diff --git a/pattern.c b/pattern.c
  7805. index 4cdbd05..9375a4e 100644
  7806. --- a/pattern.c
  7807. +++ b/pattern.c
  7808. @@ -92,6 +92,9 @@ Flags[] =
  7809. { 'U', M_UNREAD, 0, NULL },
  7810. { 'v', M_COLLAPSED, 0, NULL },
  7811. { 'V', M_CRYPT_VERIFIED, 0, NULL },
  7812. +#ifdef USE_NNTP
  7813. + { 'w', M_NEWSGROUPS, 0, eat_regexp },
  7814. +#endif
  7815. { 'x', M_REFERENCE, 0, eat_regexp },
  7816. { 'X', M_MIMEATTACH, 0, eat_range },
  7817. { 'y', M_XLABEL, 0, eat_regexp },
  7818. @@ -1213,6 +1216,10 @@ mutt_pattern_exec (struct pattern_t *pat, pattern_exec_flag flags, CONTEXT *ctx,
  7819. }
  7820. case M_UNREFERENCED:
  7821. return (pat->not ^ (h->thread && !h->thread->child));
  7822. +#ifdef USE_NNTP
  7823. + case M_NEWSGROUPS:
  7824. + return (pat->not ^ (h->env->newsgroups && patmatch (pat, h->env->newsgroups) == 0));
  7825. +#endif
  7826. }
  7827. mutt_error (_("error: unknown op %d (report this error)."), pat->op);
  7828. return (-1);
  7829. @@ -1294,6 +1301,7 @@ int mutt_pattern_func (int op, char *prompt)
  7830. progress_t progress;
  7831. strfcpy (buf, NONULL (Context->pattern), sizeof (buf));
  7832. + if (prompt || op != M_LIMIT)
  7833. if (mutt_get_field (prompt, buf, sizeof (buf), M_PATTERN | M_CLEAR) != 0 || !buf[0])
  7834. return (-1);
  7835. diff --git a/po/POTFILES.in b/po/POTFILES.in
  7836. index 3654ad1..1e499ec 100644
  7837. --- a/po/POTFILES.in
  7838. +++ b/po/POTFILES.in
  7839. @@ -47,6 +47,8 @@ mutt_ssl_gnutls.c
  7840. mutt_tunnel.c
  7841. muttlib.c
  7842. mx.c
  7843. +newsrc.c
  7844. +nntp.c
  7845. pager.c
  7846. parse.c
  7847. pattern.c
  7848. diff --git a/postpone.c b/postpone.c
  7849. index 21e96e6..5144be9 100644
  7850. --- a/postpone.c
  7851. +++ b/postpone.c
  7852. @@ -125,15 +125,26 @@ int mutt_num_postponed (int force)
  7853. if (LastModify < st.st_mtime)
  7854. {
  7855. +#ifdef USE_NNTP
  7856. + int optnews = option (OPTNEWS);
  7857. +#endif
  7858. LastModify = st.st_mtime;
  7859. if (access (Postponed, R_OK | F_OK) != 0)
  7860. return (PostCount = 0);
  7861. +#ifdef USE_NNTP
  7862. + if (optnews)
  7863. + unset_option (OPTNEWS);
  7864. +#endif
  7865. if (mx_open_mailbox (Postponed, M_NOSORT | M_QUIET, &ctx) == NULL)
  7866. PostCount = 0;
  7867. else
  7868. PostCount = ctx.msgcount;
  7869. mx_fastclose_mailbox (&ctx);
  7870. +#ifdef USE_NNTP
  7871. + if (optnews)
  7872. + set_option (OPTNEWS);
  7873. +#endif
  7874. }
  7875. return (PostCount);
  7876. diff --git a/protos.h b/protos.h
  7877. index 551b142..bb44d34 100644
  7878. --- a/protos.h
  7879. +++ b/protos.h
  7880. @@ -111,6 +111,7 @@ HASH *mutt_make_id_hash (CONTEXT *);
  7881. HASH *mutt_make_subj_hash (CONTEXT *);
  7882. LIST *mutt_make_references(ENVELOPE *e);
  7883. +LIST *mutt_parse_references (char *, int);
  7884. char *mutt_read_rfc822_line (FILE *, char *, size_t *);
  7885. ENVELOPE *mutt_read_rfc822_header (FILE *, HEADER *, short, short);
  7886. diff --git a/recvattach.c b/recvattach.c
  7887. index 31dfcad..05a1cde 100644
  7888. --- a/recvattach.c
  7889. +++ b/recvattach.c
  7890. @@ -1119,6 +1119,15 @@ void mutt_view_attachments (HEADER *hdr)
  7891. }
  7892. #endif
  7893. +#ifdef USE_NNTP
  7894. + if (Context->magic == M_NNTP)
  7895. + {
  7896. + mutt_flushinp ();
  7897. + mutt_error _("Can't delete attachment from news server.");
  7898. + break;
  7899. + }
  7900. +#endif
  7901. +
  7902. if (WithCrypto && hdr->security & ~PGP_TRADITIONAL_CHECKED)
  7903. {
  7904. mutt_message _(
  7905. @@ -1210,10 +1219,33 @@ void mutt_view_attachments (HEADER *hdr)
  7906. case OP_FORWARD_MESSAGE:
  7907. CHECK_ATTACH;
  7908. mutt_attach_forward (fp, hdr, idx, idxlen,
  7909. - menu->tagprefix ? NULL : idx[menu->current]->content);
  7910. + menu->tagprefix ? NULL : idx[menu->current]->content, 0);
  7911. menu->redraw = REDRAW_FULL;
  7912. break;
  7913. +#ifdef USE_NNTP
  7914. + case OP_FORWARD_TO_GROUP:
  7915. + CHECK_ATTACH;
  7916. + mutt_attach_forward (fp, hdr, idx, idxlen,
  7917. + menu->tagprefix ? NULL : idx[menu->current]->content, SENDNEWS);
  7918. + menu->redraw = REDRAW_FULL;
  7919. + break;
  7920. +
  7921. + case OP_FOLLOWUP:
  7922. + CHECK_ATTACH;
  7923. +
  7924. + if (!idx[menu->current]->content->hdr->env->followup_to ||
  7925. + mutt_strcasecmp (idx[menu->current]->content->hdr->env->followup_to, "poster") ||
  7926. + query_quadoption (OPT_FOLLOWUPTOPOSTER,_("Reply by mail as poster prefers?")) != M_YES)
  7927. + {
  7928. + mutt_attach_reply (fp, hdr, idx, idxlen,
  7929. + menu->tagprefix ? NULL : idx[menu->current]->content,
  7930. + SENDNEWS|SENDREPLY);
  7931. + menu->redraw = REDRAW_FULL;
  7932. + break;
  7933. + }
  7934. +#endif
  7935. +
  7936. case OP_REPLY:
  7937. case OP_GROUP_REPLY:
  7938. case OP_LIST_REPLY:
  7939. diff --git a/recvcmd.c b/recvcmd.c
  7940. index a6a3a91..e633119 100644
  7941. --- a/recvcmd.c
  7942. +++ b/recvcmd.c
  7943. @@ -401,7 +401,7 @@ static BODY ** copy_problematic_attachments (FILE *fp,
  7944. static void attach_forward_bodies (FILE * fp, HEADER * hdr,
  7945. ATTACHPTR ** idx, short idxlen,
  7946. BODY * cur,
  7947. - short nattach)
  7948. + short nattach, int flags)
  7949. {
  7950. short i;
  7951. short mime_fwd_all = 0;
  7952. @@ -547,7 +547,7 @@ _("Can't decode all tagged attachments. MIME-forward the others?"))) == -1)
  7953. tmpfp = NULL;
  7954. /* now that we have the template, send it. */
  7955. - ci_send_message (0, tmphdr, tmpbody, NULL, parent);
  7956. + ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
  7957. return;
  7958. bail:
  7959. @@ -574,7 +574,7 @@ _("Can't decode all tagged attachments. MIME-forward the others?"))) == -1)
  7960. */
  7961. static void attach_forward_msgs (FILE * fp, HEADER * hdr,
  7962. - ATTACHPTR ** idx, short idxlen, BODY * cur)
  7963. + ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
  7964. {
  7965. HEADER *curhdr = NULL;
  7966. HEADER *tmphdr;
  7967. @@ -679,23 +679,23 @@ static void attach_forward_msgs (FILE * fp, HEADER * hdr,
  7968. else
  7969. mutt_free_header (&tmphdr);
  7970. - ci_send_message (0, tmphdr, *tmpbody ? tmpbody : NULL,
  7971. + ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL,
  7972. NULL, curhdr);
  7973. }
  7974. void mutt_attach_forward (FILE * fp, HEADER * hdr,
  7975. - ATTACHPTR ** idx, short idxlen, BODY * cur)
  7976. + ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
  7977. {
  7978. short nattach;
  7979. if (check_all_msg (idx, idxlen, cur, 0) == 0)
  7980. - attach_forward_msgs (fp, hdr, idx, idxlen, cur);
  7981. + attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
  7982. else
  7983. {
  7984. nattach = count_tagged (idx, idxlen);
  7985. - attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach);
  7986. + attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
  7987. }
  7988. }
  7989. @@ -753,28 +753,40 @@ attach_reply_envelope_defaults (ENVELOPE *env, ATTACHPTR **idx, short idxlen,
  7990. return -1;
  7991. }
  7992. - if (parent)
  7993. +#ifdef USE_NNTP
  7994. + if ((flags & SENDNEWS))
  7995. {
  7996. - if (mutt_fetch_recips (env, curenv, flags) == -1)
  7997. - return -1;
  7998. + /* in case followup set Newsgroups: with Followup-To: if it present */
  7999. + if (!env->newsgroups && curenv &&
  8000. + mutt_strcasecmp (curenv->followup_to, "poster"))
  8001. + env->newsgroups = safe_strdup (curenv->followup_to);
  8002. }
  8003. else
  8004. +#endif
  8005. {
  8006. - for (i = 0; i < idxlen; i++)
  8007. + if (parent)
  8008. {
  8009. - if (idx[i]->content->tagged
  8010. - && mutt_fetch_recips (env, idx[i]->content->hdr->env, flags) == -1)
  8011. + if (mutt_fetch_recips (env, curenv, flags) == -1)
  8012. return -1;
  8013. }
  8014. + else
  8015. + {
  8016. + for (i = 0; i < idxlen; i++)
  8017. + {
  8018. + if (idx[i]->content->tagged
  8019. + && mutt_fetch_recips (env, idx[i]->content->hdr->env, flags) == -1)
  8020. + return -1;
  8021. + }
  8022. + }
  8023. +
  8024. + if ((flags & SENDLISTREPLY) && !env->to)
  8025. + {
  8026. + mutt_error _("No mailing lists found!");
  8027. + return (-1);
  8028. + }
  8029. +
  8030. + mutt_fix_reply_recipients (env);
  8031. }
  8032. -
  8033. - if ((flags & SENDLISTREPLY) && !env->to)
  8034. - {
  8035. - mutt_error _("No mailing lists found!");
  8036. - return (-1);
  8037. - }
  8038. -
  8039. - mutt_fix_reply_recipients (env);
  8040. mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
  8041. if (parent)
  8042. @@ -835,6 +847,13 @@ void mutt_attach_reply (FILE * fp, HEADER * hdr,
  8043. char prefix[SHORT_STRING];
  8044. int rc;
  8045. +#ifdef USE_NNTP
  8046. + if (flags & SENDNEWS)
  8047. + set_option (OPTNEWSSEND);
  8048. + else
  8049. + unset_option (OPTNEWSSEND);
  8050. +#endif
  8051. +
  8052. if (check_all_msg (idx, idxlen, cur, 0) == -1)
  8053. {
  8054. nattach = count_tagged (idx, idxlen);
  8055. diff --git a/send.c b/send.c
  8056. index 893c859..13358b9 100644
  8057. --- a/send.c
  8058. +++ b/send.c
  8059. @@ -44,6 +44,11 @@
  8060. #include <sys/types.h>
  8061. #include <utime.h>
  8062. +#ifdef USE_NNTP
  8063. +#include "nntp.h"
  8064. +#include "mx.h"
  8065. +#endif
  8066. +
  8067. #ifdef MIXMASTER
  8068. #include "remailer.h"
  8069. #endif
  8070. @@ -213,17 +218,51 @@ static int edit_address (ADDRESS **a, /* const */ char *field)
  8071. return 0;
  8072. }
  8073. -static int edit_envelope (ENVELOPE *en)
  8074. +static int edit_envelope (ENVELOPE *en, int flags)
  8075. {
  8076. char buf[HUGE_STRING];
  8077. LIST *uh = UserHeader;
  8078. - if (edit_address (&en->to, "To: ") == -1 || en->to == NULL)
  8079. - return (-1);
  8080. - if (option (OPTASKCC) && edit_address (&en->cc, "Cc: ") == -1)
  8081. - return (-1);
  8082. - if (option (OPTASKBCC) && edit_address (&en->bcc, "Bcc: ") == -1)
  8083. - return (-1);
  8084. +#ifdef USE_NNTP
  8085. + if (option (OPTNEWSSEND))
  8086. + {
  8087. + if (en->newsgroups)
  8088. + strfcpy (buf, en->newsgroups, sizeof (buf));
  8089. + else
  8090. + buf[0] = 0;
  8091. + if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) != 0)
  8092. + return (-1);
  8093. + FREE (&en->newsgroups);
  8094. + en->newsgroups = safe_strdup (buf);
  8095. +
  8096. + if (en->followup_to)
  8097. + strfcpy (buf, en->followup_to, sizeof (buf));
  8098. + else
  8099. + buf[0] = 0;
  8100. + if (option (OPTASKFOLLOWUP) && mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) != 0)
  8101. + return (-1);
  8102. + FREE (&en->followup_to);
  8103. + en->followup_to = safe_strdup (buf);
  8104. +
  8105. + if (en->x_comment_to)
  8106. + strfcpy (buf, en->x_comment_to, sizeof (buf));
  8107. + else
  8108. + buf[0] = 0;
  8109. + if (option (OPTXCOMMENTTO) && option (OPTASKXCOMMENTTO) && mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) != 0)
  8110. + return (-1);
  8111. + FREE (&en->x_comment_to);
  8112. + en->x_comment_to = safe_strdup (buf);
  8113. + }
  8114. + else
  8115. +#endif
  8116. + {
  8117. + if (edit_address (&en->to, "To: ") == -1 || en->to == NULL)
  8118. + return (-1);
  8119. + if (option (OPTASKCC) && edit_address (&en->cc, "Cc: ") == -1)
  8120. + return (-1);
  8121. + if (option (OPTASKBCC) && edit_address (&en->bcc, "Bcc: ") == -1)
  8122. + return (-1);
  8123. + }
  8124. if (en->subject)
  8125. {
  8126. @@ -258,6 +297,14 @@ static int edit_envelope (ENVELOPE *en)
  8127. return 0;
  8128. }
  8129. +#ifdef USE_NNTP
  8130. +char *nntp_get_header (const char *s)
  8131. +{
  8132. + SKIPWS (s);
  8133. + return safe_strdup (s);
  8134. +}
  8135. +#endif
  8136. +
  8137. static void process_user_recips (ENVELOPE *env)
  8138. {
  8139. LIST *uh = UserHeader;
  8140. @@ -270,6 +317,14 @@ static void process_user_recips (ENVELOPE *env)
  8141. env->cc = rfc822_parse_adrlist (env->cc, uh->data + 3);
  8142. else if (ascii_strncasecmp ("bcc:", uh->data, 4) == 0)
  8143. env->bcc = rfc822_parse_adrlist (env->bcc, uh->data + 4);
  8144. +#ifdef USE_NNTP
  8145. + else if (ascii_strncasecmp ("newsgroups:", uh->data, 11) == 0)
  8146. + env->newsgroups = nntp_get_header (uh->data + 11);
  8147. + else if (ascii_strncasecmp ("followup-to:", uh->data, 12) == 0)
  8148. + env->followup_to = nntp_get_header (uh->data + 12);
  8149. + else if (ascii_strncasecmp ("x-comment-to:", uh->data, 13) == 0)
  8150. + env->x_comment_to = nntp_get_header (uh->data + 13);
  8151. +#endif
  8152. }
  8153. }
  8154. @@ -308,6 +363,12 @@ static void process_user_header (ENVELOPE *env)
  8155. else if (ascii_strncasecmp ("to:", uh->data, 3) != 0 &&
  8156. ascii_strncasecmp ("cc:", uh->data, 3) != 0 &&
  8157. ascii_strncasecmp ("bcc:", uh->data, 4) != 0 &&
  8158. +#ifdef USE_NNTP
  8159. + ascii_strncasecmp ("newsgroups:", uh->data, 11) != 0 &&
  8160. + ascii_strncasecmp ("followup-to:", uh->data, 12) != 0 &&
  8161. + ascii_strncasecmp ("x-comment-to:", uh->data, 13) != 0 &&
  8162. +#endif
  8163. + ascii_strncasecmp ("supersedes:", uh->data, 11) != 0 &&
  8164. ascii_strncasecmp ("subject:", uh->data, 8) != 0 &&
  8165. ascii_strncasecmp ("return-path:", uh->data, 12) != 0)
  8166. {
  8167. @@ -656,6 +717,10 @@ void mutt_add_to_reference_headers (ENVELOPE *env, ENVELOPE *curenv, LIST ***pp,
  8168. if (pp) *pp = p;
  8169. if (qq) *qq = q;
  8170. +#ifdef USE_NNTP
  8171. + if (option (OPTNEWSSEND) && option (OPTXCOMMENTTO) && curenv->from)
  8172. + env->x_comment_to = safe_strdup (mutt_get_name (curenv->from));
  8173. +#endif
  8174. }
  8175. static void
  8176. @@ -718,6 +783,16 @@ envelope_defaults (ENVELOPE *env, CONTEXT *ctx, HEADER *cur, int flags)
  8177. if (flags & SENDREPLY)
  8178. {
  8179. +#ifdef USE_NNTP
  8180. + if ((flags & SENDNEWS))
  8181. + {
  8182. + /* in case followup set Newsgroups: with Followup-To: if it present */
  8183. + if (!env->newsgroups && curenv &&
  8184. + mutt_strcasecmp (curenv->followup_to, "poster"))
  8185. + env->newsgroups = safe_strdup (curenv->followup_to);
  8186. + }
  8187. + else
  8188. +#endif
  8189. if (tag)
  8190. {
  8191. HEADER *h;
  8192. @@ -864,7 +939,18 @@ void mutt_set_followup_to (ENVELOPE *e)
  8193. * it hasn't already been set
  8194. */
  8195. - if (option (OPTFOLLOWUPTO) && !e->mail_followup_to)
  8196. + if (!option (OPTFOLLOWUPTO))
  8197. + return;
  8198. +#ifdef USE_NNTP
  8199. + if (option (OPTNEWSSEND))
  8200. + {
  8201. + if (!e->followup_to && e->newsgroups && (strrchr (e->newsgroups, ',')))
  8202. + e->followup_to = safe_strdup (e->newsgroups);
  8203. + return;
  8204. + }
  8205. +#endif
  8206. +
  8207. + if (!e->mail_followup_to)
  8208. {
  8209. if (mutt_is_list_cc (0, e->to, e->cc))
  8210. {
  8211. @@ -1026,6 +1112,9 @@ static int send_message (HEADER *msg)
  8212. #endif
  8213. #if USE_SMTP
  8214. +#ifdef USE_NNTP
  8215. + if (!option (OPTNEWSSEND))
  8216. +#endif
  8217. if (SmtpUrl)
  8218. return mutt_smtp_send (msg->env->from, msg->env->to, msg->env->cc,
  8219. msg->env->bcc, tempfile,
  8220. @@ -1137,6 +1226,13 @@ ci_send_message (int flags, /* send mode */
  8221. int rv = -1;
  8222. +#ifdef USE_NNTP
  8223. + if (flags & SENDNEWS)
  8224. + set_option (OPTNEWSSEND);
  8225. + else
  8226. + unset_option (OPTNEWSSEND);
  8227. +#endif
  8228. +
  8229. if (!flags && !msg && quadoption (OPT_RECALL) != M_NO &&
  8230. mutt_num_postponed (1))
  8231. {
  8232. @@ -1167,6 +1263,22 @@ ci_send_message (int flags, /* send mode */
  8233. {
  8234. if ((flags = mutt_get_postponed (ctx, msg, &cur, fcc, sizeof (fcc))) < 0)
  8235. goto cleanup;
  8236. +#ifdef USE_NNTP
  8237. + /*
  8238. + * If postponed message is a news article, it have
  8239. + * a "Newsgroups:" header line, then set appropriate flag.
  8240. + */
  8241. + if (msg->env->newsgroups)
  8242. + {
  8243. + flags |= SENDNEWS;
  8244. + set_option (OPTNEWSSEND);
  8245. + }
  8246. + else
  8247. + {
  8248. + flags &= ~SENDNEWS;
  8249. + unset_option (OPTNEWSSEND);
  8250. + }
  8251. +#endif
  8252. }
  8253. if (flags & (SENDPOSTPONED|SENDRESEND))
  8254. @@ -1259,11 +1371,16 @@ ci_send_message (int flags, /* send mode */
  8255. if (flags & SENDREPLY)
  8256. mutt_fix_reply_recipients (msg->env);
  8257. +#ifdef USE_NNTP
  8258. + if ((flags & SENDNEWS) && ctx && ctx->magic == M_NNTP && !msg->env->newsgroups)
  8259. + msg->env->newsgroups = safe_strdup (((NNTP_DATA *)ctx->data)->group);
  8260. +#endif
  8261. +
  8262. if (! (flags & (SENDMAILX|SENDBATCH)) &&
  8263. ! (option (OPTAUTOEDIT) && option (OPTEDITHDRS)) &&
  8264. ! ((flags & SENDREPLY) && option (OPTFASTREPLY)))
  8265. {
  8266. - if (edit_envelope (msg->env) == -1)
  8267. + if (edit_envelope (msg->env, flags) == -1)
  8268. goto cleanup;
  8269. }
  8270. @@ -1546,6 +1663,11 @@ main_loop:
  8271. if (i == -1)
  8272. {
  8273. /* abort */
  8274. +#ifdef USE_NNTP
  8275. + if (flags & SENDNEWS)
  8276. + mutt_message _("Article not posted.");
  8277. + else
  8278. +#endif
  8279. mutt_message _("Mail not sent.");
  8280. goto cleanup;
  8281. }
  8282. @@ -1578,6 +1700,9 @@ main_loop:
  8283. }
  8284. }
  8285. +#ifdef USE_NNTP
  8286. + if (!(flags & SENDNEWS))
  8287. +#endif
  8288. if (!has_recips (msg->env->to) && !has_recips (msg->env->cc) &&
  8289. !has_recips (msg->env->bcc))
  8290. {
  8291. @@ -1611,6 +1736,19 @@ main_loop:
  8292. mutt_error _("No subject specified.");
  8293. goto main_loop;
  8294. }
  8295. +#ifdef USE_NNTP
  8296. + if ((flags & SENDNEWS) && !msg->env->subject)
  8297. + {
  8298. + mutt_error _("No subject specified.");
  8299. + goto main_loop;
  8300. + }
  8301. +
  8302. + if ((flags & SENDNEWS) && !msg->env->newsgroups)
  8303. + {
  8304. + mutt_error _("No newsgroup specified.");
  8305. + goto main_loop;
  8306. + }
  8307. +#endif
  8308. if (msg->content->next)
  8309. msg->content = mutt_make_multipart (msg->content);
  8310. @@ -1817,7 +1955,12 @@ full_fcc:
  8311. }
  8312. }
  8313. else if (!option (OPTNOCURSES) && ! (flags & SENDMAILX))
  8314. - mutt_message (i == 0 ? _("Mail sent.") : _("Sending in background."));
  8315. + mutt_message (i != 0 ? _("Sending in background.") :
  8316. +#ifdef USE_NNTP
  8317. + (flags & SENDNEWS) ? _("Article posted.") : _("Mail sent."));
  8318. +#else
  8319. + _("Mail sent."));
  8320. +#endif
  8321. if (WithCrypto && (msg->security & ENCRYPT))
  8322. FREE (&pgpkeylist);
  8323. diff --git a/sendlib.c b/sendlib.c
  8324. index 9d83401..46fc511 100644
  8325. --- a/sendlib.c
  8326. +++ b/sendlib.c
  8327. @@ -46,6 +46,10 @@
  8328. #include <sys/wait.h>
  8329. #include <fcntl.h>
  8330. +#ifdef USE_NNTP
  8331. +#include "nntp.h"
  8332. +#endif
  8333. +
  8334. #ifdef HAVE_SYSEXITS_H
  8335. #include <sysexits.h>
  8336. #else /* Make sure EX_OK is defined <philiph@pobox.com> */
  8337. @@ -1543,6 +1547,14 @@ void mutt_write_references (LIST *r, FILE *f, int trim)
  8338. {
  8339. LIST **ref = NULL;
  8340. int refcnt = 0, refmax = 0;
  8341. + int multiline = 1;
  8342. + int space = 0;
  8343. +
  8344. + if (trim < 0)
  8345. + {
  8346. + trim = -trim;
  8347. + multiline = 0;
  8348. + }
  8349. for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
  8350. {
  8351. @@ -1553,9 +1565,11 @@ void mutt_write_references (LIST *r, FILE *f, int trim)
  8352. while (refcnt-- > 0)
  8353. {
  8354. - fputc (' ', f);
  8355. + if (multiline || space)
  8356. + fputc (' ', f);
  8357. + space = 1;
  8358. fputs (ref[refcnt]->data, f);
  8359. - if (refcnt >= 1)
  8360. + if (multiline && refcnt >= 1)
  8361. fputc ('\n', f);
  8362. }
  8363. @@ -1962,6 +1976,9 @@ int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
  8364. mutt_write_address_list (env->to, fp, 4, 0);
  8365. }
  8366. else if (mode > 0)
  8367. +#ifdef USE_NNTP
  8368. + if (!option (OPTNEWSSEND))
  8369. +#endif
  8370. fputs ("To: \n", fp);
  8371. if (env->cc)
  8372. @@ -1970,6 +1987,9 @@ int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
  8373. mutt_write_address_list (env->cc, fp, 4, 0);
  8374. }
  8375. else if (mode > 0)
  8376. +#ifdef USE_NNTP
  8377. + if (!option (OPTNEWSSEND))
  8378. +#endif
  8379. fputs ("Cc: \n", fp);
  8380. if (env->bcc && should_write_bcc)
  8381. @@ -1981,8 +2001,28 @@ int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
  8382. }
  8383. }
  8384. else if (mode > 0)
  8385. +#ifdef USE_NNTP
  8386. + if (!option (OPTNEWSSEND))
  8387. +#endif
  8388. fputs ("Bcc: \n", fp);
  8389. +#ifdef USE_NNTP
  8390. + if (env->newsgroups)
  8391. + fprintf (fp, "Newsgroups: %s\n", env->newsgroups);
  8392. + else if (mode == 1 && option (OPTNEWSSEND))
  8393. + fputs ("Newsgroups: \n", fp);
  8394. +
  8395. + if (env->followup_to)
  8396. + fprintf (fp, "Followup-To: %s\n", env->followup_to);
  8397. + else if (mode == 1 && option (OPTNEWSSEND))
  8398. + fputs ("Followup-To: \n", fp);
  8399. +
  8400. + if (env->x_comment_to)
  8401. + fprintf (fp, "X-Comment-To: %s\n", env->x_comment_to);
  8402. + else if (mode == 1 && option (OPTNEWSSEND) && option (OPTXCOMMENTTO))
  8403. + fputs ("X-Comment-To: \n", fp);
  8404. +#endif
  8405. +
  8406. if (env->subject)
  8407. mutt_write_one_header (fp, "Subject", env->subject, NULL, 0, 0);
  8408. else if (mode == 1)
  8409. @@ -2001,6 +2041,9 @@ int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
  8410. fputs ("Reply-To: \n", fp);
  8411. if (env->mail_followup_to)
  8412. +#ifdef USE_NNTP
  8413. + if (!option (OPTNEWSSEND))
  8414. +#endif
  8415. {
  8416. fputs ("Mail-Followup-To: ", fp);
  8417. mutt_write_address_list (env->mail_followup_to, fp, 18, 0);
  8418. @@ -2344,6 +2387,23 @@ mutt_invoke_sendmail (ADDRESS *from, /* the sender */
  8419. size_t argslen = 0, argsmax = 0;
  8420. int i;
  8421. +#ifdef USE_NNTP
  8422. + if (option (OPTNEWSSEND))
  8423. + {
  8424. + char cmd[LONG_STRING];
  8425. +
  8426. + mutt_FormatString (cmd, sizeof (cmd), 0, NONULL (Inews), nntp_format_str, 0, 0);
  8427. + if (!*cmd)
  8428. + {
  8429. + i = nntp_post (msg);
  8430. + unlink (msg);
  8431. + return i;
  8432. + }
  8433. +
  8434. + s = safe_strdup (cmd);
  8435. + }
  8436. +#endif
  8437. +
  8438. /* ensure that $sendmail is set to avoid a crash. http://dev.mutt.org/trac/ticket/3548 */
  8439. if (!s)
  8440. {
  8441. @@ -2374,6 +2434,10 @@ mutt_invoke_sendmail (ADDRESS *from, /* the sender */
  8442. i++;
  8443. }
  8444. +#ifdef USE_NNTP
  8445. + if (!option (OPTNEWSSEND))
  8446. + {
  8447. +#endif
  8448. if (eightbit && option (OPTUSE8BITMIME))
  8449. args = add_option (args, &argslen, &argsmax, "-B8BITMIME");
  8450. @@ -2405,6 +2469,9 @@ mutt_invoke_sendmail (ADDRESS *from, /* the sender */
  8451. args = add_args (args, &argslen, &argsmax, to);
  8452. args = add_args (args, &argslen, &argsmax, cc);
  8453. args = add_args (args, &argslen, &argsmax, bcc);
  8454. +#ifdef USE_NNTP
  8455. + }
  8456. +#endif
  8457. if (argslen == argsmax)
  8458. safe_realloc (&args, sizeof (char *) * (++argsmax));
  8459. @@ -2485,6 +2552,9 @@ void mutt_prepare_envelope (ENVELOPE *env, int final)
  8460. rfc2047_encode_string (&env->x_label);
  8461. if (env->subject)
  8462. +#ifdef USE_NNTP
  8463. + if (!option (OPTNEWSSEND) || option (OPTMIMESUBJECT))
  8464. +#endif
  8465. {
  8466. rfc2047_encode_string (&env->subject);
  8467. }
  8468. @@ -2605,6 +2675,10 @@ int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
  8469. }
  8470. rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
  8471. +#ifdef USE_NNTP
  8472. + unset_option (OPTNEWSSEND);
  8473. +#endif
  8474. +
  8475. /*
  8476. * prepare recipient list. idna conversion appears to happen before this
  8477. * function is called, since the user receives confirmation of the address
  8478. diff --git a/sort.c b/sort.c
  8479. index 76e9e79..9aa259c 100644
  8480. --- a/sort.c
  8481. +++ b/sort.c
  8482. @@ -24,6 +24,11 @@
  8483. #include "sort.h"
  8484. #include "mutt_idna.h"
  8485. +#ifdef USE_NNTP
  8486. +#include "mx.h"
  8487. +#include "nntp.h"
  8488. +#endif
  8489. +
  8490. #include <stdlib.h>
  8491. #include <string.h>
  8492. #include <ctype.h>
  8493. @@ -151,6 +156,17 @@ static int compare_order (const void *a, const void *b)
  8494. HEADER **ha = (HEADER **) a;
  8495. HEADER **hb = (HEADER **) b;
  8496. +#ifdef USE_NNTP
  8497. + if (Context && Context->magic == M_NNTP)
  8498. + {
  8499. + anum_t na = NHDR (*ha)->article_num;
  8500. + anum_t nb = NHDR (*hb)->article_num;
  8501. + int result = na == nb ? 0 : na > nb ? 1 : -1;
  8502. + AUXSORT (result, a, b);
  8503. + return (SORTCODE (result));
  8504. + }
  8505. + else
  8506. +#endif
  8507. /* no need to auxsort because you will never have equality here */
  8508. return (SORTCODE ((*ha)->index - (*hb)->index));
  8509. }
  8510. diff --git a/url.c b/url.c
  8511. index 988cf59..3fd4079 100644
  8512. --- a/url.c
  8513. +++ b/url.c
  8514. @@ -39,6 +39,8 @@ static const struct mapping_t UrlMap[] =
  8515. { "imaps", U_IMAPS },
  8516. { "pop", U_POP },
  8517. { "pops", U_POPS },
  8518. + { "news", U_NNTP },
  8519. + { "snews", U_NNTPS },
  8520. { "mailto", U_MAILTO },
  8521. { "smtp", U_SMTP },
  8522. { "smtps", U_SMTPS },
  8523. @@ -214,7 +216,7 @@ int url_ciss_tostring (ciss_url_t* ciss, char* dest, size_t len, int flags)
  8524. safe_strcat (dest, len, "//");
  8525. len -= (l = strlen (dest)); dest += l;
  8526. - if (ciss->user)
  8527. + if (ciss->user && (ciss->user[0] || !(flags & U_PATH)))
  8528. {
  8529. char u[STRING];
  8530. url_pct_encode (u, sizeof (u), ciss->user);
  8531. diff --git a/url.h b/url.h
  8532. index 926416e..15ec9ce 100644
  8533. --- a/url.h
  8534. +++ b/url.h
  8535. @@ -8,6 +8,8 @@ typedef enum url_scheme
  8536. U_POPS,
  8537. U_IMAP,
  8538. U_IMAPS,
  8539. + U_NNTP,
  8540. + U_NNTPS,
  8541. U_SMTP,
  8542. U_SMTPS,
  8543. U_MAILTO,