aboutsummaryrefslogblamecommitdiffstats
path: root/modules/itip-formatter/itip-view.c
blob: 32ac52dc44700767f567de6d1035c2d244c5416f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
  



                                                                
  



                                                                    
  
                                                                   
                                                                             
  
  



                                                        







                    
                       
                             
                                          



                                
                                    

                                              
                                    



                                       
                                             

























                                                  
                                   

                              



                                         


























































































                                                                     

                           











































































































































































































































































































































































































































































































































































































                                                                                                                                                                                 
                                                             



































































                                                                                      
                                                                                    




                                                                  






                                              
                                                                                























                                                                               

                                        
                                                                         
                                                                                               




                                        

                                                                                                              




           
                                                


                                                   







                                                         





























































                                                                           
                                                                                       



















                                                                        
                                                             










                                                      
                                                                                


                                
                                                                                                                  













                                                                                 





                                                             


                                             
                                                                  













                                                                         
                                                            

                                                            
                                                    

                                                            
                                                                        

















                                                                                    
                           
 
                                                      



                                      
                                        















                                                                                  
                                        


                                               

                                                                    
                                
                                         


                                                       
                                                                












                                                                                        











                                                                  









                                                                                
 




                                                                               
                                              
                                                   





                                                                 







                                                                    
 

                                      







































                                                             

                                                       
 

                                                            
 
                                                               








                                            

                                                    
                                                   
                                                            

                               

                                                      
                                                   
                                                            












                                                                       



                                                            


                                                     



                                                              













                                                                       
                                                
                                             





                                                       
                                             


                                                         

         


                                             











                                                                  
                                              
 
                                             










































                                                                      
                                   
                                  
                          

                                  





                                                              
 

                                                     
                                                              
                                                         
 

                                                       
                                                                


                                                           




















                                                                      











                                                            






                                                                     




























                                                                 

                                           


                                                         
                                        




























                                                                        
                                           
                                 
 


                                                                     
 

                                


                                                                               

                         



                                                                              

                         




                                                                                  

                         
                                                           
                                                          










                                                                                     

                         
                                                                                           
                                                        

                         

                         




                                                                                                         

                         
                                                                                           
                                                        
 

                         
                                                           
                                                          
 

                         








                                                                                                     

                                


                                                                    

                                                       


































                                                                                               

                         


                                                                                    





                                                                                           
 

                                                   

                                          

                         
                                                           
                                                          
 
                                        

                                           
                                        

                                                      
                                        

                                                                   
                                        

                                                               
                                        

                                                  
                                        


                                                    
                                                   

                         





                                                                                               
 

                                                   









                                                                             
                                                      



















































































































                                                                                        
                                          


                       

                                                                      

                                        
                                             




































































































































                                                                                                       
                                                                                 















































































































































































































































































































































































































































































































































































































































                                                                                                      
                                                                                          

















                                                                         
                                        
                                                       

                       
































                                                                          

                                               




                                     





















                                                                   
                                                                          











































                                                                       
                                                         


























































































































                                                                                              
 




                                                 
 
                                               
 

                                      
 



                                                                 
 




                                                                         
 



                                           
 
                                                         
 

                                      
 

                                                                 
 


                                                                                
 


                                                            
 




                                                    
 

                                                  
 




                                                    
 
                                               
 
                                                                                      
 
                                                  
 

                                      
 



































                                                                    


































































































































































































































                                                                                      

                                 

                                                                            
 
                                            
                                                     






























                                                                          

                                                                    






                                                                                
 
                
                            
                       
                                       
                                  
                            


                                  

                   
 
                    
 
                   
                     
 

                                                            
                     

                                        

                           






                                                                                          

                                
 
                                                                     







                                                                  
                                          


                              
                                  
         
 



                     

                                                  





                            


                                                                                          
                                     

                                             














                                                                                       
                 

                              
 



                    

                                          

                                                
 



                                       
 
                                        
                                                          
 
                                          
                       
 
                                                                  


                                                       

                                                           



                                                                                    
                                                                                               

                                                

                 
 
                                          

                       
                                                                           
                                                                         
 





                                                                    
 

                                                                            
 


                                                          
 

                                                                                   
                                                                                     
 
                                                                 
 
                                                                            
                              
                                                   
 
                                                           
 


                                                                                     
                                                          



                                                                                                         

                 




                                                                 
                                          
                       
 



                                                                            

          



                                                                          
           
 
                                                                         
 





                                                                    
 

                                                                            
 


                                                                    
 

                                                                                   
                                                                                     
 
                                                                 
 
                                                                            
                              
                                                   
 
                                                           



                                                                                     
                                                          



                                                                                                         
                 
 



                                                                 


           

                                        
                                            
 
                                  
                           
                           
                         
                             
                                    


                                             
 

                                        




                                                                                     
                                                              


                                                                           
                                   

         
                                                                               
                    
                                                                    



                                                                                                 





                                                         
 



                                                                           
                                                          
                                                                         
 




                                                                    



                                                                            
                                 
 


                                                                                                           


                              
         


                                                                 

                                        

 
                      
                                    
 

                                   
 

                                                                        
                                                                                          
 
                    


                            
                                            


           
                                  
                                 

                                 
 


                                          
                                    
                          
 






                                                                     
 


                                                              
 



                                                          
           
                                            
                                      
 

                                  

                                                                                    
 
                                                                                            

 
           
                                       
                                            
 
                                        
                                         
 

                                                                        


           


                                                
 

                             
                        
                             
 


                                               

                                                                




                                                        
 


                                                                                
                                     
                          
 
                                   
                                                     
                                     
                          
         
 
                                                                               



                                                                           
 
                                                                
                                                                        
         
 
                                                            


                                                           
                               
                                                                      

                                                                  
         
 
                                                      
 
                                            


                                 
                               

 
           
                                            
                                      



                                                 
 

                                           
 
                                          
 


















                                                                      

 
           
                                                   
                                             

                                                        
 
                        
 
                                                      
 

                                                                          

                                       

                                                  
                                             
                                        
         


           


                                    
 
                                    
 
                                                      
 

                                          


                                                 
                                     


           

                                           
 
                             
                       
                        
 
                                      
 
                         
                        
 


                                                         
 
                                                                                 
 
                                                                            




                                                               

         
                                                                                             
                                                                           

                                              
                                                                                 
 
                                                   
 


                                                                                           
                                                                     


                                                                                               


                                                                   
                                                        

                                            
                                                              

                                                            
                                                                                                             
 















                                                                                                             
                                                    

                                                       
                                          
                                                                  
 
                                                                           
                                                                                       
                                                                                                  
 
                                                         
                                                                            
                            
                                                                             

                 
                                                                    
                                                                                                                                     
                                                                                                                                       
                                                                         
                            
                                                                          
 
                                                                          
                 
         
 
 





                                           
                                                                                 
 
                                                                              
                                              
                                                
                                          
 
                                                                                 
                                            
 

                                                                               

                                                                                

                                                                        
                                                                                                             
                                                                                                      


                                            
                                                                   

                                                                                             
                                                                   
 
                                                                                                   
                                                
                                                                   
                                               
                                                    
 
                                              
                                                                     
                                                                                     

                                                                    
                                                                                      

                                                                    
                                                                                      

                                              
                                                               

                         
                                                                                   
                                                                      

                                                                            
 
                                          
                                                        
                                                                        
 
                                             
                                                                    
                                                        
 
                                                                                     
                                

                                                                                                                                        
                         
                                                    
                                              
                                                             


                                                                                          
                                      
                                                            


                                                                                        
                                      
                                                            


                                                                                        




                                                        
                 
         
 

                                                     
                                                                                  
                                                 
                                                      
                                          
                                 
                                 

                                          




                            


                                                        








                                                                                    
                                                                                


                                                                        






                                                    
                       

                                    







                                                                                                                               



                                                                                      
                                                                                                               

                 




                                                    




                                            


                                                     








                                                                                    
                                                                                

                                                                        
                                       






                                                    
                       

                                    







                                                                                                                               



                                                                                      
                                                                                                               

                 




                                                    









                                                                                                                          


                                                 








                                                                                        





                                                           
                    

                                                                                        














                                                                                                                  


                                           

                                           
                                        
                                  
                        
                        
                               

                                              

                             

                                                                




                                                        
 


                                                                                


                                        

         





                                                           



                                                                    
                                                     
                                        
                                     


                       






                                                
                                           
 
                                              
 









                                                                            


                                                         




                                                      



                                     



                                                          






                                
                                                           
                                                         




                                              
                                  
                            
                                 
 
                                      

                          
                                 




                                       
 

                                                 













                                                                      

                                                      
 
                                             
                                                           
 


                                                        
                                                                     
 
                                                                         
 
                                                      
 



                                                                    
 


                                                                      
 




                                                                                    
 


                                                                      
 

                                           
 




                                                                      
 
                                 

                 
 
                             
                                     
 

                                                                         
                                                                   
                
                            

                                                                         
                                                                                    
         
 

                                                         

                          
                                                         

                                                            
                                         
                                                       

                                                                                 

                                                                  
                                                                                                
                                                                                         
                                                 
                                      

                                                        




                                                                                






                                                                                  





                                       
                                                                                         

                                             


                                                                 
                    


                                                                 
         
 


                                                                          
                     

 
               

                                         

                                             











                                                                              
 









                                                                                  

                                                      
 

                                                                           
 
                                                                           
                                                                     
 
                                                                    
                                                                 


                                                                                  
 

                                                                    


                                                 






                    

                                          

                                    

                       
 


                       
                                                       
 
                                                                   
 

                              
 



                                                                                 
                                                            
                                                                                                        
 


                                                               
                                                                              


         





































































































                                                                              
           
                                                
                                          
                                                

                                                  
                                                                                             


                                                        
                                                                     


                                               
                                                                                          



                                                     





                                                                  



           
                                                      
                                                
                                                    
 
                                                   

                                                                        
                                        



                                           

                                      







                                                           
                                                                              




































                                                                                                  
                                                            










                                                                           

                                         


                                              
 
                                    
                                                     


                                                                             
                                                








                                                          
                                                      



                                              

                                               


                                                                  
                                   
                                                              

                             


                                                                     

                                                                                         



                                                                            
                                                                                       

                                                                           
                 
                                     


                       
                                                  
 
                                                


                                              

                                                            
                                                                                                    

                                          

                                                            
                                                                                                     


                                                                                           

                                                            
                                                                                                    


                                                                                           

                                                            
                                                                                                    





                                        
                                                              


           
                                  
                            
                                       



                                  
                                  
                   
 
                                                                                                     
 









                                                                                   
                                                

                                        






                                                                    
                                                              











                                                                                          

                                                                     
                                                              



                                                                     

                             
 
                                                          








                                                                            

                                                                            

















                                                                                                         
                                                   
                                                              
                                                                        
                                                   
 
                                                                               
 

                                                       
                                             



                                            
                                                                       
                                                                                     
 

                                                                 
 

                                                                                                
                                                                                                 
                                                         
 


                                                                                                            
                                 
 
                                                     
 
                                                                           

                                                                                                


                                                                                                            

                                 



                                                                                                   
                 
 
                                                                    
                                           
 
                                                                                  
         
 
                                               
 





                                         
 
         
                                                                 
                                    

 

                                                                    

                                   
 
                           

                                
 




                                                                                                    
                                                                         








                                                                           
                                                                     
 
                                                                        



















                                                                               

                                                      



                                            

                        



                                                                


                                       
 







                                                                        
 
                                                       


                                                       
 

                                   
                      

 
           
                                      
                                


                                        

                        




                                                            

                                                  
                                        
                                     

                                                           
                          
                                       
                                             
                                                                    
                                                                   

                          

                                                            
                                                                        
                

                                                            
                                                                                    
         




                         
           

                                    





                                                                                               
                                                                   










                                                                                                
           
                                      

                                       
 
                                                       
                                   
                                                              
                             
 
                                                                   
 






                                                                                


                                                              
                                                                     
                                                                   

                                                
                
                                                              


                                                            



           
                                                      
                                                
                                                         






                                                    
                                                                  
 


                                                                  
 

                                                             
                                                                           









                                                                               
                                                               




                                                                                                                                                                                                             




                                                                                



                                                                                                                 
                                                                                                                    




                                                     
 



                                                                




                                                                                        
                                                                           


                                                                                                                            

                                                                                 





                                                                                       



                                                             
                                 
 


                                                                                               
 
                                                                   
                                                       
                                                                               

                                                                             
                                                                      
                                        


                                                                                                     


                                                                                                                 




                                                                                     
 

                                                                                                      
 

                                                                                                                 
                                        
                                                       
                                                                              

                                                                             
                                 
 
                                                              

                         


                                     
 
                                          
                                                       
                                
                                             


                                                       

         
                                                                                                     
 





                                                                 



                              
 

                                                                      

                                                                       

                                                       
                                   
                                                              







                                                                                        
                 
 
                                       
 


                                                                                  




                                                                                                   
                                                                



                                                                   

                                                                    

                                                       
                                   
                                                              










                                                                                        
                 








                                                                          


                                                                                          




                                                                                                           






                                                                         


                             

         
                                                                


           
                                             
                                       







                                                                  
                                                                                                     

                                                                                             





                                                              
 
                     

 
           
                                
                          



                                     
 
                           
                                
                                             


                                                       

                                      
                                      
                                                     

                                                                    
                                                               
                              
                                                    

                                                                    
                                                            
                              
                                                    

                                                                    
                                                            




                                                
                
                                      
                                                     

                                                                     
                                                                                                     
                              
                                                    

                                                                     
                                                                                               
                              
                                                    

                                                                     
                                                                                               




                                                


         











                                                
                            







                                                      




















                                                            
                                        
 


                                
                                
 



                                                    





                                                                             

                                        
                                                                                          


                         





                                                             

 

                               
                                     

                                       
 
                     
 
                                 


                                       
                                    
 
                                                    
 
                       


               
                                        
                                  
                                         
 
                            



                                                    

                                  
                            

                                      
                                

                                
                                                                

                                                                                                                  



                             


                                                                      
                                                                                

                                
                                                                

                                                                                                                  
 




                                                              

                             
 






















                                                                                             

                                                       



                                                                   

                                
                                                                   

                                                                                                                                      
 
                             
         
 
                                                       
                                   
                                                              









                                                                                                                            

                                  
                                                             
                      
                                     
                                                             
                      
                

                                
                                                                   


                                                                                                                                      

                             
 


                                                                                                    
                                                                                                   
 
                               
 

                                
                                                                           

                                                                                                               
 
                                 
                                   
                
                                   
         
 
                                                                              

                                  
                            

                                                                                                     

                                                          

                                      
 



                                                                                      

                                             
                                              
 
                                                                                                              
                                                                                  
 

                                                                                                  
                 
                                    
                            
 


                                                                                            
                                                    
 

                                                                
 







                                                                                                 
 

                                                                                                   
 
                                                                       

                                                                                                     

                                        
                                                        
 
                                                                                                
                                                                                       

                                                                                              
                                                        
                 




                                                                                                             

         

                                                                                 

                                             
 

                                
                                                                   

                                                                                                                                      
 
                             
          

                                                        
 



                                                                          

                                   
                                          
                              
                                    

                                                  



                                                               










                                                                                        
                                                

                                                                          
                                              

                                                                        
                                             



                                                                       







                                                                   

                                  

                                                              

                    
 
 
               
                            
 
                                    






                                                                                          
 

                            
 



                                                 


                       
                     

 
           
                                 

                                            
 
                                    
                                
                           

                                        




                                                  
                                                                                            
                                                               




                                                                                                           
 


                                                                                                      
 

                                                                                                                                                              
 

                                                                  
                                                     
                                                  


                                                                                
 
                           
                                               
                                                                          
                                                        
                                                             

                                                          
                                                                

                                              

                                                                     
                                                                    


                                                  
                                                
                                                             


                                                                 

                                                                     
                                                                    


                                                
                                                                          
                                                        
                                                             

                                                          
                                                                
                              
                                                                




                                                                                    

                                                                     
                                                                    


                                               
                                                             

                                               
                                                            

                                                
                                                

                                             



                                                               



                               

 
               
                                           




                                                                                
                                    



                                                              






                                                                                       
               
                                      
 




                                      
                                  

                                 
                            
                                       
                            
 
                           
                             
 
                                       
                                                




                                                                    






                                                              
                                                                 

                                                                                      
                                               
                                                                                       


                                                                                        


                                                                           




                                                                                                       


                                                                      
         
 


                   

                                    
 
                      
                                   


                                         


                                     

                                
                                
                                  
                                  
                                     



                                        
 
                                       
                                                        
 
                                                         
 

                                                           
 


                                                                                    
 
                                                           

                                
                                                                   
                


                                                                                                                      
 
                                       








                                                                                               




                                                                



























                                                                                              
                 
         
 
                                                   
 
                               
                                       




























                                                                                                                                                              
 



                                                                                      
 
                                                              
 
                                                                                                                                         
 

                                                                                                                           
 





                                                                                                                                                              
 





                                                                                  
                 
         
 
                                                        
                                                                                       
 
                                                           
                                              
 
                                                  

                                                                      
                                   

                                                              
                                            
                                                            
                                                                                   

                                                             
                                                                                               

                                                            
                                                                                   

                                                             
                                                                                    

                                              
                                                                                  
                         
                 

                                                          
 
                                             



                                                                                     
                                                                     

                                                             
 













                                                                                   
                 

                                                      
 
                                                                 

                                                  
 
                                            
                                                             
                                     
                                                                                
         
 
                                              

                      











                                                                   
                                              
                              
         
 

















                                                                               
 

                                                            

                                   
 

                                                                                   

                                                                     
                                                                  
                                                                                                
                    
                                         
 

                                                                                             
                                                                               
                                                                                            
         
 
                                                                  
 
                                   
                                                             
                                             
                                                                                                   


                                                              

                                                                          
                                                                   


                                                                    
 

                                                          

                                 
 

                                                                                   

                                                                     
                                                                  
                                                                                                

                                         
 
                                              

                                                                                     



                                                                      
                                                                                           
 
                                                                           
                                                                                          


                                                  

                                                
                                                           
                                                                          
                                     
                                                             
                                                                                                                              

                                                            
                                                                                                                           

                                                            
                                                                                                                           



                                                        
                 
         
 


                                                     
 
                               
                                                                                                                                                                                          
 
                                         
                                                                                                  
                        

                                                             
                 







                                                                           
         
 
/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 *
 * Authors:
 *      JP Rosevear <jpr@novell.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <glib/gi18n.h>
#include <webkit/webkitdom.h>
#include <libedataserver/libedataserver.h>

#include <shell/e-shell.h>
#include <shell/e-shell-utils.h>

#include <calendar/gui/itip-utils.h>

#include <libemail-engine/mail-folder-cache.h>
#include <libemail-engine/mail-mt.h>
#include <libemail-engine/mail-tools.h>

#include <mail/em-config.h>
#include <mail/em-utils.h>
#include <em-format/e-mail-formatter-utils.h>

#include <calendar/gui/itip-utils.h>

#include "e-conflict-search-selector.h"
#include "e-source-conflict-search.h"
#include "itip-view.h"
#include "e-mail-part-itip.h"

#define d(x)

#define MEETING_ICON "stock_new-meeting"

#define ITIP_VIEW_GET_PRIVATE(obj) \
    (G_TYPE_INSTANCE_GET_PRIVATE \
    ((obj), ITIP_TYPE_VIEW, ItipViewPrivate))

G_DEFINE_TYPE (ItipView, itip_view, G_TYPE_OBJECT)

typedef struct  {
    ItipViewInfoItemType type;
    gchar *message;

    guint id;
} ItipViewInfoItem;

struct _ItipViewPrivate {
    EClientCache *client_cache;
    gchar *extension_name;

    ESourceRegistry *registry;
    gulong source_added_handler_id;
    gulong source_removed_handler_id;

    ItipViewMode mode;
    ECalClientSourceType type;

        gchar *sender;
    gchar *organizer;
    gchar *organizer_sentby;
    gchar *delegator;
    gchar *attendee;
    gchar *attendee_sentby;
    gchar *proxy;

    gchar *summary;

    gchar *location;
        gchar *status;
    gchar *comment;

    struct tm *start_tm;
    gint start_tm_is_date : 1;
        gchar *start_label;
        const gchar *start_header;

    struct tm *end_tm;
    gint end_tm_is_date : 1;
        gchar *end_label;
        const gchar *end_header;

    GSList *upper_info_items;
    GSList *lower_info_items;

    guint next_info_item_id;

    gchar *description;

    gint buttons_sensitive : 1;

        gboolean is_recur_set;

    gint needs_decline : 1;

        WebKitDOMDocument *dom_document;
        EMailPartItip *itip_part;

        gchar *error;
};

#define TEXT_ROW_SENDER "text_row_sender"
#define TABLE_ROW_SUMMARY "table_row_summary"
#define TABLE_ROW_LOCATION "table_row_location"
#define TABLE_ROW_START_DATE "table_row_start_time"
#define TABLE_ROW_END_DATE "table_row_end_time"
#define TABLE_ROW_STATUS "table_row_status"
#define TABLE_ROW_COMMENT "table_row_comment"
#define TABLE_ROW_DESCRIPTION "table_row_description"
#define TABLE_ROW_RSVP_COMMENT "table_row_rsvp_comment"
#define TABLE_ROW_ESCB "table_row_escb"
#define TABLE_ROW_BUTTONS "table_row_buttons"
#define TABLE_ROW_ESCB_LABEL "table_row_escb_label"

#define TABLE_BUTTONS "table_buttons"

#define SELECT_ESOURCE "select_esource"
#define TEXTAREA_RSVP_COMMENT "textarea_rsvp_comment"

#define CHECKBOX_RSVP "checkbox_rsvp"
#define CHECKBOX_RECUR "checkbox_recur"
#define CHECKBOX_UPDATE "checkbox_update"
#define CHECKBOX_FREE_TIME "checkbox_free_time"
#define CHECKBOX_KEEP_ALARM "checkbox_keep_alarm"
#define CHECKBOX_INHERIT_ALARM "checkbox_inherit_alarm"

#define BUTTON_OPEN_CALENDAR "button_open_calendar"
#define BUTTON_DECLINE "button_decline"
#define BUTTON_DECLINE_ALL "button_decline_all"
#define BUTTON_ACCEPT "button_accept"
#define BUTTON_ACCEPT_ALL "button_accept_all"
#define BUTTON_TENTATIVE "button_tentative"
#define BUTTON_TENTATIVE_ALL "button_tentative_all"
#define BUTTON_SEND_INFORMATION "button_send_information"
#define BUTTON_UPDATE "button_update"
#define BUTTON_UPDATE_ATTENDEE_STATUS "button_update_attendee_status"
#define BUTTON_SAVE "button_save"

#define TABLE_UPPER_ITIP_INFO "table_upper_itip_info"
#define TABLE_LOWER_ITIP_INFO "table_lower_itip_info"

#define DIV_ITIP_CONTENT "div_itip_content"
#define DIV_ITIP_ERROR "div_itip_error"

enum {
    PROP_0,
    PROP_CLIENT_CACHE,
    PROP_EXTENSION_NAME
};

enum {
    SOURCE_SELECTED,
    RESPONSE,
    LAST_SIGNAL
};

static guint signals[LAST_SIGNAL] = { 0 };

static void
format_date_and_time_x (struct tm *date_tm,
                        struct tm *current_tm,
                        gboolean use_24_hour_format,
                        gboolean show_midnight,
                        gboolean show_zero_seconds,
                        gboolean is_date,
                        gchar *buffer,
                        gint buffer_size)
{
    gchar *format;
    struct tm tomorrow_tm, week_tm;

    /* Calculate a normalized "tomorrow" */
    tomorrow_tm = *current_tm;
    /* Don't need this if date is in the past. Also, year assumption won't fail. */
    if (date_tm->tm_year >= current_tm->tm_year && tomorrow_tm.tm_mday == time_days_in_month (current_tm->tm_year + 1900, current_tm->tm_mon)) {
        tomorrow_tm.tm_mday = 1;
        if (tomorrow_tm.tm_mon == 11) {
            tomorrow_tm.tm_mon = 1;
            tomorrow_tm.tm_year++;
        } else {
            tomorrow_tm.tm_mon++;
        }
    } else {
        tomorrow_tm.tm_mday++;
    }

    /* Calculate a normalized "next seven days" */
    week_tm = *current_tm;
    /* Don't need this if date is in the past. Also, year assumption won't fail. */
    if (date_tm->tm_year >= current_tm->tm_year && week_tm.tm_mday + 6 > time_days_in_month (date_tm->tm_year + 1900, date_tm->tm_mon)) {
        week_tm.tm_mday = (week_tm.tm_mday + 6) % time_days_in_month (date_tm->tm_year + 1900, date_tm->tm_mon);
        if (week_tm.tm_mon == 11) {
            week_tm.tm_mon = 1;
            week_tm.tm_year++;
        } else {
            week_tm.tm_mon++;
        }
    } else {
        week_tm.tm_mday += 6;
    }

    /* Today */
    if (date_tm->tm_mday == current_tm->tm_mday &&
        date_tm->tm_mon == current_tm->tm_mon &&
        date_tm->tm_year == current_tm->tm_year) {
        if (is_date || (!show_midnight && date_tm->tm_hour == 0
            && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
            /* strftime format of a weekday and a date. */
            format = _("Today");
        } else if (use_24_hour_format) {
            if (!show_zero_seconds && date_tm->tm_sec == 0)
                /* strftime format of a time,
                 * in 24-hour format, without seconds. */
                format = _("Today %H:%M");
            else
                /* strftime format of a time,
                 * in 24-hour format. */
                format = _("Today %H:%M:%S");
        } else {
            if (!show_zero_seconds && date_tm->tm_sec == 0)
                /* strftime format of a time,
                 * in 12-hour format, without seconds. */
                format = _("Today %l:%M %p");
            else
                /* strftime format of a time,
                 * in 12-hour format. */
                format = _("Today %l:%M:%S %p");
        }

    /* Tomorrow */
    } else if (date_tm->tm_mday == tomorrow_tm.tm_mday &&
           date_tm->tm_mon == tomorrow_tm.tm_mon &&
           date_tm->tm_year == tomorrow_tm.tm_year) {
        if (is_date || (!show_midnight && date_tm->tm_hour == 0
            && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
            /* strftime format of a weekday and a date. */
            format = _("Tomorrow");
        } else if (use_24_hour_format) {
            if (!show_zero_seconds && date_tm->tm_sec == 0)
                /* strftime format of a time,
                 * in 24-hour format, without seconds. */
                format = _("Tomorrow %H:%M");
            else
                /* strftime format of a time,
                 * in 24-hour format. */
                format = _("Tomorrow %H:%M:%S");
        } else {
            if (!show_zero_seconds && date_tm->tm_sec == 0)
                /* strftime format of a time,
                 * in 12-hour format, without seconds. */
                format = _("Tomorrow %l:%M %p");
            else
                /* strftime format of a time,
                 * in 12-hour format. */
                format = _("Tomorrow %l:%M:%S %p");
        }

    /* Within 6 days */
    } else if ((date_tm->tm_year >= current_tm->tm_year &&
            date_tm->tm_mon >= current_tm->tm_mon &&
            date_tm->tm_mday >= current_tm->tm_mday) &&

           (date_tm->tm_year < week_tm.tm_year ||

           (date_tm->tm_year == week_tm.tm_year &&
            date_tm->tm_mon < week_tm.tm_mon) ||

           (date_tm->tm_year == week_tm.tm_year &&
            date_tm->tm_mon == week_tm.tm_mon &&
            date_tm->tm_mday < week_tm.tm_mday))) {
        if (is_date || (!show_midnight && date_tm->tm_hour == 0
            && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
            /* strftime format of a weekday. */
            format = _("%A");
        } else if (use_24_hour_format) {
            if (!show_zero_seconds && date_tm->tm_sec == 0)
                /* strftime format of a weekday and a
                 * time, in 24-hour format, without seconds. */
                format = _("%A %H:%M");
            else
                /* strftime format of a weekday and a
                 * time, in 24-hour format. */
                format = _("%A %H:%M:%S");
        } else {
            if (!show_zero_seconds && date_tm->tm_sec == 0)
                /* strftime format of a weekday and a
                 * time, in 12-hour format, without seconds. */
                format = _("%A %l:%M %p");
            else
                /* strftime format of a weekday and a
                 * time, in 12-hour format. */
                format = _("%A %l:%M:%S %p");
        }

    /* This Year */
    } else if (date_tm->tm_year == current_tm->tm_year) {
        if (is_date || (!show_midnight && date_tm->tm_hour == 0
            && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
            /* strftime format of a weekday and a date
             * without a year. */
            format = _("%A, %B %e");
        } else if (use_24_hour_format) {
            if (!show_zero_seconds && date_tm->tm_sec == 0)
                /* strftime format of a weekday, a date
                 * without a year and a time,
                 * in 24-hour format, without seconds. */
                format = _("%A, %B %e %H:%M");
            else
                /* strftime format of a weekday, a date without a year
                 * and a time, in 24-hour format. */
                format = _("%A, %B %e %H:%M:%S");
        } else {
            if (!show_zero_seconds && date_tm->tm_sec == 0)
                /* strftime format of a weekday, a date without a year
                 * and a time, in 12-hour format, without seconds. */
                format = _("%A, %B %e %l:%M %p");
            else
                /* strftime format of a weekday, a date without a year
                 * and a time, in 12-hour format. */
                format = _("%A, %B %e %l:%M:%S %p");
        }
    } else {
        if (is_date || (!show_midnight && date_tm->tm_hour == 0
            && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
            /* strftime format of a weekday and a date. */
            format = _("%A, %B %e, %Y");
        } else if (use_24_hour_format) {
            if (!show_zero_seconds && date_tm->tm_sec == 0)
                /* strftime format of a weekday, a date and a
                 * time, in 24-hour format, without seconds. */
                format = _("%A, %B %e, %Y %H:%M");
            else
                /* strftime format of a weekday, a date and a
                 * time, in 24-hour format. */
                format = _("%A, %B %e, %Y %H:%M:%S");
        } else {
            if (!show_zero_seconds && date_tm->tm_sec == 0)
                /* strftime format of a weekday, a date and a
                 * time, in 12-hour format, without seconds. */
                format = _("%A, %B %e, %Y %l:%M %p");
            else
                /* strftime format of a weekday, a date and a
                 * time, in 12-hour format. */
                format = _("%A, %B %e, %Y %l:%M:%S %p");
        }
    }

    /* strftime returns 0 if the string doesn't fit, and leaves the buffer
     * undefined, so we set it to the empty string in that case. */
    if (e_utf8_strftime_fix_am_pm (buffer, buffer_size, format, date_tm) == 0)
        buffer[0] = '\0';
}

static gchar *
dupe_first_bold (const gchar *format,
                 const gchar *first,
                 const gchar *second)
{
    gchar *f, *s, *res;

    f = g_markup_printf_escaped ("<b>%s</b>", first ? first : "");
    s = g_markup_escape_text (second ? second : "", -1);

    res = g_strdup_printf (format, f, s);

    g_free (f);
    g_free (s);

    return res;
}

static gchar *
set_calendar_sender_text (ItipView *view)
{
    ItipViewPrivate *priv;
    const gchar *organizer, *attendee;
    gchar *sender = NULL;
    gchar *on_behalf_of = NULL;

    priv = view->priv;

    organizer = priv->organizer ? priv->organizer : _("An unknown person");
    attendee = priv->attendee ? priv->attendee : _("An unknown person");

    /* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
    if (priv->organizer && priv->proxy)
        on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
    else if (priv->attendee && priv->proxy)
        on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);

    switch (priv->mode) {
    case ITIP_VIEW_MODE_PUBLISH:
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s has published the following meeting information:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s has published the following meeting information:"), organizer, NULL);
        break;
    case ITIP_VIEW_MODE_REQUEST:
        /* FIXME is the delegator stuff handled correctly here? */
        if (priv->delegator) {
            sender = dupe_first_bold (_("%s has delegated the following meeting to you:"), priv->delegator, NULL);
        } else {
            if (priv->organizer_sentby)
                sender = dupe_first_bold (_("%s through %s requests your presence at the following meeting:"), organizer, priv->organizer_sentby);
            else
                sender = dupe_first_bold (_("%s requests your presence at the following meeting:"), organizer, NULL);
        }
        break;
    case ITIP_VIEW_MODE_ADD:
        /* FIXME What text for this? */
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s wishes to add to an existing meeting:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s wishes to add to an existing meeting:"), organizer, NULL);
        break;
    case ITIP_VIEW_MODE_REFRESH:
        if (priv->attendee_sentby)
            sender = dupe_first_bold (_("%s through %s wishes to receive the latest information for the following meeting:"), attendee, priv->attendee_sentby);
        else
            sender = dupe_first_bold (_("%s wishes to receive the latest information for the following meeting:"), attendee, NULL);
        break;
    case ITIP_VIEW_MODE_REPLY:
        if (priv->attendee_sentby)
            sender = dupe_first_bold (_("%s through %s has sent back the following meeting response:"), attendee, priv->attendee_sentby);
        else
            sender = dupe_first_bold (_("%s has sent back the following meeting response:"), attendee, NULL);
        break;
    case ITIP_VIEW_MODE_CANCEL:
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s has canceled the following meeting:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s has canceled the following meeting:"), organizer, NULL);
        break;
    case ITIP_VIEW_MODE_COUNTER:
        if (priv->attendee_sentby)
            sender = dupe_first_bold (_("%s through %s has proposed the following meeting changes."), attendee, priv->attendee_sentby);
        else
            sender = dupe_first_bold (_("%s has proposed the following meeting changes:"), attendee, NULL);
        break;
    case ITIP_VIEW_MODE_DECLINECOUNTER:
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s has declined the following meeting changes:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s has declined the following meeting changes:"), organizer, NULL);
        break;
    default:
        break;
    }

    if (sender && on_behalf_of) {
        gchar *tmp;
        tmp = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
        g_free (sender);
        sender = tmp;
    }

    g_free (on_behalf_of);

    return sender;
}

static gchar *
set_tasklist_sender_text (ItipView *view)
{
    ItipViewPrivate *priv;
    const gchar *organizer, *attendee;
    gchar *sender = NULL;
    gchar *on_behalf_of = NULL;

    priv = view->priv;

    organizer = priv->organizer ? priv->organizer : _("An unknown person");
    attendee = priv->attendee ? priv->attendee : _("An unknown person");

    /* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
    if (priv->organizer && priv->proxy)
        on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
    else if (priv->attendee && priv->proxy)
        on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);

    switch (priv->mode) {
    case ITIP_VIEW_MODE_PUBLISH:
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s has published the following task:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s has published the following task:"), organizer, NULL);
        break;
    case ITIP_VIEW_MODE_REQUEST:
        /* FIXME is the delegator stuff handled correctly here? */
        if (priv->delegator) {
            sender = dupe_first_bold (_("%s requests the assignment of %s to the following task:"), organizer, priv->delegator);
        } else {
            if (priv->organizer_sentby)
                sender = dupe_first_bold (_("%s through %s has assigned you a task:"), organizer, priv->organizer_sentby);
            else
                sender = dupe_first_bold (_("%s has assigned you a task:"), organizer, NULL);
        }
        break;
    case ITIP_VIEW_MODE_ADD:
        /* FIXME What text for this? */
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s wishes to add to an existing task:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s wishes to add to an existing task:"), organizer, NULL);
        break;
    case ITIP_VIEW_MODE_REFRESH:
        if (priv->attendee_sentby)
            sender = dupe_first_bold (_("%s through %s wishes to receive the latest information for the following assigned task:"), attendee, priv->attendee_sentby);
        else
            sender = dupe_first_bold (_("%s wishes to receive the latest information for the following assigned task:"), attendee, NULL);
        break;
    case ITIP_VIEW_MODE_REPLY:
        if (priv->attendee_sentby)
            sender = dupe_first_bold (_("%s through %s has sent back the following assigned task response:"), attendee, priv->attendee_sentby);
        else
            sender = dupe_first_bold (_("%s has sent back the following assigned task response:"), attendee, NULL);
        break;
    case ITIP_VIEW_MODE_CANCEL:
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s has canceled the following assigned task:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s has canceled the following assigned task:"), organizer, NULL);
        break;
    case ITIP_VIEW_MODE_COUNTER:
        if (priv->attendee_sentby)
            sender = dupe_first_bold (_("%s through %s has proposed the following task assignment changes:"), attendee, priv->attendee_sentby);
        else
            sender = dupe_first_bold (_("%s has proposed the following task assignment changes:"), attendee, NULL);
        break;
    case ITIP_VIEW_MODE_DECLINECOUNTER:
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s has declined the following assigned task:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s has declined the following assigned task:"), organizer, NULL);
        break;
    default:
        break;
    }

    if (sender && on_behalf_of) {
        gchar *tmp;
        tmp = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
        g_free (sender);
        sender = tmp;
    }

    g_free (on_behalf_of);

    return sender;
}

static gchar *
set_journal_sender_text (ItipView *view)
{
    ItipViewPrivate *priv;
    const gchar *organizer;
    gchar *sender = NULL;
    gchar *on_behalf_of = NULL;

    priv = view->priv;

    organizer = priv->organizer ? priv->organizer : _("An unknown person");

    /* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
    if (priv->organizer && priv->proxy)
        on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
    else if (priv->attendee && priv->proxy)
        on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);

    switch (priv->mode) {
    case ITIP_VIEW_MODE_PUBLISH:
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s has published the following memo:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s has published the following memo:"), organizer, NULL);
        break;
    case ITIP_VIEW_MODE_ADD:
        /* FIXME What text for this? */
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s wishes to add to an existing memo:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s wishes to add to an existing memo:"), organizer, NULL);
        break;
    case ITIP_VIEW_MODE_CANCEL:
        if (priv->organizer_sentby)
            sender = dupe_first_bold (_("%s through %s has canceled the following shared memo:"), organizer, priv->organizer_sentby);
        else
            sender = dupe_first_bold (_("%s has canceled the following shared memo:"), organizer, NULL);
        break;
    default:
        break;
    }

    if (sender && on_behalf_of)
        sender = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);

    g_free (on_behalf_of);

    return sender;
}

static void
set_sender_text (ItipView *view)
{
    ItipViewPrivate *priv;
    priv = view->priv;

    if (priv->sender)
        g_free (priv->sender);

    switch (priv->type) {
    case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
        priv->sender = set_calendar_sender_text (view);
        break;
    case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
        priv->sender = set_tasklist_sender_text (view);
        break;
    case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
        priv->sender = set_journal_sender_text (view);
        break;
    default:
        priv->sender = NULL;
        break;
    }

    if (priv->sender && priv->dom_document) {
        WebKitDOMElement *div;

        div = webkit_dom_document_get_element_by_id (
            priv->dom_document, TEXT_ROW_SENDER);
        webkit_dom_html_element_set_inner_html (
            WEBKIT_DOM_HTML_ELEMENT (div), priv->sender, NULL);
    }
}

static void
update_start_end_times (ItipView *view)
{
    ItipViewPrivate *priv;
    WebKitDOMElement *row, *col;
    gchar buffer[256];
    time_t now;
    struct tm *now_tm;

    priv = view->priv;

    now = time (NULL);
    now_tm = localtime (&now);

    if (priv->start_label)
        g_free (priv->start_label);
    if (priv->end_label)
        g_free (priv->end_label);

    #define is_same(_member) (priv->start_tm->_member == priv->end_tm->_member)
    if (priv->start_tm && priv->end_tm && priv->start_tm_is_date && priv->end_tm_is_date
        && is_same (tm_mday) && is_same (tm_mon) && is_same (tm_year)) {
        /* it's an all day event in one particular day */
        format_date_and_time_x (priv->start_tm, now_tm, FALSE, TRUE, FALSE, priv->start_tm_is_date, buffer, 256);
        priv->start_label = g_strdup (buffer);
        priv->start_header = _("All day:");
        priv->end_header = NULL;
        priv->end_label = NULL;
    } else {
        if (priv->start_tm) {
            format_date_and_time_x (priv->start_tm, now_tm, FALSE, TRUE, FALSE, priv->start_tm_is_date, buffer, 256);
            priv->start_header = priv->start_tm_is_date ? _("Start day:") : _("Start time:");
            priv->start_label = g_strdup (buffer);
        } else {
            priv->start_header = NULL;
            priv->start_label = NULL;
        }

        if (priv->end_tm) {
            format_date_and_time_x (priv->end_tm, now_tm, FALSE, TRUE, FALSE, priv->end_tm_is_date, buffer, 256);
            priv->end_header = priv->end_tm_is_date ? _("End day:") : _("End time:");
            priv->end_label = g_strdup (buffer);
        } else {
            priv->end_header = NULL;
            priv->end_label = NULL;
        }
    }
    #undef is_same

    if (priv->dom_document) {
        row = webkit_dom_document_get_element_by_id (
            priv->dom_document, TABLE_ROW_START_DATE);
        if (priv->start_header && priv->start_label) {
            webkit_dom_html_element_set_hidden (
                WEBKIT_DOM_HTML_ELEMENT (row), FALSE);

            col = webkit_dom_element_get_first_element_child (row);
            webkit_dom_html_element_set_inner_html (
                WEBKIT_DOM_HTML_ELEMENT (col), priv->start_header, NULL);

            col = webkit_dom_element_get_last_element_child (row);
            webkit_dom_html_element_set_inner_html (
                WEBKIT_DOM_HTML_ELEMENT (col), priv->start_label, NULL);
        } else {
            webkit_dom_html_element_set_hidden (
                WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
        }

        row = webkit_dom_document_get_element_by_id (
            priv->dom_document, TABLE_ROW_END_DATE);
        if (priv->end_header && priv->end_label) {
            webkit_dom_html_element_set_hidden (
                WEBKIT_DOM_HTML_ELEMENT (row), FALSE);

            col = webkit_dom_element_get_first_element_child (row);
            webkit_dom_html_element_set_inner_html (
                WEBKIT_DOM_HTML_ELEMENT (col), priv->end_header, NULL);

            col = webkit_dom_element_get_last_element_child (row);
            webkit_dom_html_element_set_inner_html (
                WEBKIT_DOM_HTML_ELEMENT (col), priv->end_label, NULL);
        } else {
            webkit_dom_html_element_set_hidden (
                WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
        }
    }
}

static void
button_clicked_cb (WebKitDOMElement *element,
                   WebKitDOMEvent *event,
                   gpointer data)
{
    ItipViewResponse response;
    gchar *responseStr;

    responseStr = webkit_dom_html_button_element_get_value (
        WEBKIT_DOM_HTML_BUTTON_ELEMENT (element));

    response = atoi (responseStr);

    g_signal_emit (data, signals[RESPONSE], 0, response);
}

static void
rsvp_toggled_cb (WebKitDOMHTMLInputElement *input,
                 WebKitDOMEvent *event,
                 gpointer data)
{
    WebKitDOMElement *el;

    ItipView *view = data;
    gboolean rsvp;

    rsvp = webkit_dom_html_input_element_get_checked (input);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
    webkit_dom_html_text_area_element_set_disabled (
        WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
}

static void
recur_toggled_cb (WebKitDOMHTMLInputElement *input,
                  WebKitDOMEvent *event,
                  gpointer data)
{
    ItipView *view = data;

    itip_view_set_mode (view, view->priv->mode);
}

/*
  alarm_check_toggled_cb
  check1 was changed, so make the second available based on state of the first check.
*/
static void
alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1,
                        WebKitDOMEvent *event,
                        ItipView *view)
{
    WebKitDOMElement *check2;
    gchar *id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (check1));

    if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) {
        check2 = webkit_dom_document_get_element_by_id (
            view->priv->dom_document, CHECKBOX_KEEP_ALARM);
    } else {
        check2 = webkit_dom_document_get_element_by_id (
            view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
    }

    g_free (id);

    webkit_dom_html_input_element_set_disabled (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (check2),
        (webkit_dom_html_element_get_hidden (
                WEBKIT_DOM_HTML_ELEMENT (check1)) &&
            webkit_dom_html_input_element_get_checked (check1)));
}

static void
source_changed_cb (WebKitDOMElement *select,
                   WebKitDOMEvent *event,
                   ItipView *view)
{
    ESource *source;

    source = itip_view_ref_source (view);

    d (printf ("Source changed to '%s'\n", e_source_get_display_name (source)));
    g_signal_emit (view, signals[SOURCE_SELECTED], 0, source);

    g_object_unref (source);
}

static void
append_checkbox_table_row (GString *buffer,
                           const gchar *name,
                           const gchar *label)
{
    gchar *access_key, *html_label;

    html_label = e_mail_formatter_parse_html_mnemonics (label, &access_key);

    g_string_append_printf (
        buffer,
        "<tr id=\"table_row_%s\" hidden=\"\"><td colspan=\"2\">"
        "<input type=\"checkbox\" name=\"%s\" id=\"%s\" value=\"%s\" >"
        "<label for=\"%s\" accesskey=\"%s\">%s</label>"
        "</td></tr>\n",
        name, name, name, name, name,
        access_key ? access_key : "", html_label);

    g_free (html_label);

    if (access_key)
        g_free (access_key);
}

static void
append_text_table_row (GString *buffer,
                       const gchar *id,
                       const gchar *label,
                       const gchar *value)
{
    if (label && *label) {

        g_string_append_printf (
            buffer,
            "<tr id=\"%s\" %s><th>%s</th><td>%s</td></tr>\n",
            id, (value && *value) ? "" : "hidden=\"\"", label, value ? value : "");

    } else {

        g_string_append_printf (
            buffer,
            "<tr id=\"%s\"%s><td colspan=\"2\">%s</td></tr>\n",
            id, g_strcmp0 (id, TABLE_ROW_SUMMARY) == 0 ? "" : " hidden=\"\"", value ? value : "");

    }
}

static void
append_text_table_row_nonempty (GString *buffer,
                                const gchar *id,
                                const gchar *label,
                                const gchar *value)
{
    if (!value || !*value)
        return;

    append_text_table_row (buffer, id, label, value);
}

static void
append_info_item_row (ItipView *view,
                      const gchar *table_id,
                      ItipViewInfoItem *item)
{
    WebKitDOMElement *table;
    WebKitDOMHTMLElement *row, *cell;
    const gchar *icon_name;
    gchar *id;

    table = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, table_id);
    row = webkit_dom_html_table_element_insert_row (
        WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);

    id = g_strdup_printf ("%s_row_%d", table_id, item->id);
    webkit_dom_html_element_set_id (row, id);
    g_free (id);

    switch (item->type) {
        case ITIP_VIEW_INFO_ITEM_TYPE_INFO:
            icon_name = GTK_STOCK_DIALOG_INFO;
            break;
        case ITIP_VIEW_INFO_ITEM_TYPE_WARNING:
            icon_name = GTK_STOCK_DIALOG_WARNING;
            break;
        case ITIP_VIEW_INFO_ITEM_TYPE_ERROR:
            icon_name = GTK_STOCK_DIALOG_ERROR;
            break;
        case ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS:
            icon_name = GTK_STOCK_FIND;
            break;
        case ITIP_VIEW_INFO_ITEM_TYPE_NONE:
            default:
            icon_name = NULL;
    }

    cell = webkit_dom_html_table_row_element_insert_cell (
        (WebKitDOMHTMLTableRowElement *) row, -1, NULL);

    if (icon_name) {
        WebKitDOMElement *image;
        gchar *icon_uri;

        image = webkit_dom_document_create_element (
            view->priv->dom_document, "IMG", NULL);

        icon_uri = g_strdup_printf ("gtk-stock://%s", icon_name);
        webkit_dom_html_image_element_set_src (
            WEBKIT_DOM_HTML_IMAGE_ELEMENT (image), icon_uri);
        g_free (icon_uri);

        webkit_dom_node_append_child (
            WEBKIT_DOM_NODE (cell),
            WEBKIT_DOM_NODE (image),
            NULL);
    }

    cell = webkit_dom_html_table_row_element_insert_cell (
        (WebKitDOMHTMLTableRowElement *) row, -1, NULL);

    webkit_dom_html_element_set_inner_html (cell, item->message, NULL);

    d (printf ("Added row %s_row_%d ('%s')\n", table_id, item->id, item->message));
}

static void
remove_info_item_row (ItipView *view,
                      const gchar *table_id,
                      guint id)
{
    WebKitDOMElement *row;
    gchar *row_id;

    row_id = g_strdup_printf ("%s_row_%d", table_id, id);
    row = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, row_id);
    g_free (row_id);

    webkit_dom_node_remove_child (
        webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)),
        WEBKIT_DOM_NODE (row),
        NULL);

    d (printf ("Removed row %s_row_%d\n", table_id, id));
}

static void
buttons_table_write_button (GString *buffer,
                            const gchar *name,
                            const gchar *label,
                            const gchar *icon,
                            ItipViewResponse response)
{
    gchar *access_key, *html_label;

    html_label = e_mail_formatter_parse_html_mnemonics (label, &access_key);

    g_string_append_printf (
        buffer,
        "<td><button type=\"button\" name=\"%s\" value=\"%d\" id=\"%s\" accesskey=\"%s\" hidden disabled>"
        "<div><img src=\"gtk-stock://%s?size=%d\"> <span>%s</span></div>"
        "</button></td>\n",
        name, response, name, access_key ? access_key : "" , icon,
        GTK_ICON_SIZE_BUTTON, html_label);

    g_free (html_label);

    if (access_key)
        g_free (access_key);
}

static void
append_buttons_table (GString *buffer)
{
    g_string_append (
        buffer,
        "<table class=\"itip buttons\" border=\"0\" "
        "id=\"" TABLE_BUTTONS "\" cellspacing=\"6\" "
        "cellpadding=\"0\" >"
        "<tr id=\"" TABLE_ROW_BUTTONS "\">");

        /* Everything gets the open button */
    buttons_table_write_button (
        buffer, BUTTON_OPEN_CALENDAR, _("Ope_n Calendar"),
        GTK_STOCK_JUMP_TO, ITIP_VIEW_RESPONSE_OPEN);
    buttons_table_write_button (
        buffer, BUTTON_DECLINE_ALL, _("_Decline all"),
        GTK_STOCK_CANCEL, ITIP_VIEW_RESPONSE_DECLINE);
    buttons_table_write_button (
        buffer, BUTTON_DECLINE, _("_Decline"),
        GTK_STOCK_CANCEL, ITIP_VIEW_RESPONSE_DECLINE);
    buttons_table_write_button (
        buffer, BUTTON_TENTATIVE_ALL, _("_Tentative all"),
        GTK_STOCK_DIALOG_QUESTION, ITIP_VIEW_RESPONSE_TENTATIVE);
    buttons_table_write_button (
        buffer, BUTTON_TENTATIVE, _("_Tentative"),
        GTK_STOCK_DIALOG_QUESTION, ITIP_VIEW_RESPONSE_TENTATIVE);
    buttons_table_write_button (
        buffer, BUTTON_ACCEPT_ALL, _("Acce_pt all"),
        GTK_STOCK_APPLY, ITIP_VIEW_RESPONSE_ACCEPT);
    buttons_table_write_button (
        buffer, BUTTON_ACCEPT, _("Acce_pt"),
        GTK_STOCK_APPLY, ITIP_VIEW_RESPONSE_ACCEPT);
    buttons_table_write_button (
        buffer, BUTTON_SEND_INFORMATION, _("Send _Information"),
        GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_REFRESH);
    buttons_table_write_button (
        buffer, BUTTON_UPDATE_ATTENDEE_STATUS, _("_Update Attendee Status"),
        GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_UPDATE);
    buttons_table_write_button (
        buffer, BUTTON_UPDATE,  _("_Update"),
        GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_CANCEL);

    g_string_append (buffer, "</tr></table>");
}

static void
itip_view_rebuild_source_list (ItipView *view)
{
    ESourceRegistry *registry;
    WebKitDOMElement *select;
    GList *list, *link;
    const gchar *extension_name;
    GHashTable *groups;

    d (printf ("Assigning a new source list!\n"));

    if (!view->priv->dom_document)
        return;

    registry = view->priv->registry;
    extension_name = itip_view_get_extension_name (view);

    select = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, SELECT_ESOURCE);

    while (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (select))) {
        webkit_dom_node_remove_child (
            WEBKIT_DOM_NODE (select),
            webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (select)),
            NULL);
    }

    if (extension_name == NULL)
        return;

    list = e_source_registry_list_sources (registry, extension_name);
    groups = g_hash_table_new_full (
        g_str_hash, g_str_equal,
        (GDestroyNotify) g_free, NULL);

    for (link = list; link != NULL; link = g_list_next (link)) {
        ESource *source = E_SOURCE (link->data);
        ESource *parent;
        WebKitDOMElement *option;
        WebKitDOMHTMLOptGroupElement *optgroup;

        parent = e_source_registry_ref_source (
            registry, e_source_get_parent (source));

        optgroup = g_hash_table_lookup (groups, e_source_get_uid (parent));
        if (!optgroup) {
            optgroup = WEBKIT_DOM_HTML_OPT_GROUP_ELEMENT (
                    webkit_dom_document_create_element (
                        view->priv->dom_document,
                        "OPTGROUP", NULL));
            webkit_dom_html_opt_group_element_set_label (
                optgroup, e_source_get_display_name (parent));
            g_hash_table_insert (
                groups, g_strdup (e_source_get_uid (parent)), optgroup);
        }
        g_object_unref (parent);

        option = webkit_dom_document_create_element (
            view->priv->dom_document, "OPTION", NULL);
        webkit_dom_html_option_element_set_value (
            WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
            e_source_get_uid (source));
        webkit_dom_html_option_element_set_label (
            WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
            e_source_get_display_name (source));
        webkit_dom_html_element_set_inner_html (
            WEBKIT_DOM_HTML_ELEMENT (option),
            e_source_get_display_name (source), NULL);

        /* See https://bugzilla.gnome.org/show_bug.cgi?id=681400
         * FIXME: This can be removed once we require WebKitGtk 1.10+ */
        #if WEBKIT_CHECK_VERSION (1, 9, 6)
            webkit_dom_element_set_class_name (
                WEBKIT_DOM_ELEMENT (option), "calendar");
        #else
            webkit_dom_html_element_set_class_name (
                WEBKIT_DOM_HTML_ELEMENT (option), "calendar");
        #endif

        if (!e_source_get_writable (source)) {
            webkit_dom_html_option_element_set_disabled (
                WEBKIT_DOM_HTML_OPTION_ELEMENT (option), TRUE);
        }

        webkit_dom_node_append_child (
            WEBKIT_DOM_NODE (optgroup),
            WEBKIT_DOM_NODE (option),
            NULL);
    }

    g_list_free_full (list, (GDestroyNotify) g_object_unref);

    list = g_hash_table_get_values (groups);
    for (link = list; link != NULL; link = g_list_next (link)) {
        WebKitDOMNode *optgroup = link->data;

        webkit_dom_node_append_child (
            WEBKIT_DOM_NODE (select), optgroup, NULL);
    }
    g_list_free (list);

    g_hash_table_destroy (groups);

    source_changed_cb (select, NULL, view);
}

static void
itip_view_source_added_cb (ESourceRegistry *registry,
                           ESource *source,
                           ItipView *view)
{
    const gchar *extension_name;

    extension_name = itip_view_get_extension_name (view);

    /* If we don't have an extension name set
     * yet then disregard the signal emission. */
    if (extension_name == NULL)
        return;

    if (e_source_has_extension (source, extension_name))
        itip_view_rebuild_source_list (view);
}

static void
itip_view_source_removed_cb (ESourceRegistry *registry,
                             ESource *source,
                             ItipView *view)
{
    const gchar *extension_name;

    extension_name = itip_view_get_extension_name (view);

    /* If we don't have an extension name set
     * yet then disregard the signal emission. */
    if (extension_name == NULL)
        return;

    if (e_source_has_extension (source, extension_name))
        itip_view_rebuild_source_list (view);
}

static void
itip_view_set_client_cache (ItipView *view,
                            EClientCache *client_cache)
{
    g_return_if_fail (E_IS_CLIENT_CACHE (client_cache));
    g_return_if_fail (view->priv->client_cache == NULL);

    view->priv->client_cache = g_object_ref (client_cache);
}

static void
itip_view_set_property (GObject *object,
                        guint property_id,
                        const GValue *value,
                        GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_CLIENT_CACHE:
            itip_view_set_client_cache (
                ITIP_VIEW (object),
                g_value_get_object (value));
            return;

        case PROP_EXTENSION_NAME:
            itip_view_set_extension_name (
                ITIP_VIEW (object),
                g_value_get_string (value));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
itip_view_get_property (GObject *object,
                        guint property_id,
                        GValue *value,
                        GParamSpec *pspec)
{
    switch (property_id) {
        case PROP_CLIENT_CACHE:
            g_value_set_object (
                value,
                itip_view_get_client_cache (
                ITIP_VIEW (object)));
            return;

        case PROP_EXTENSION_NAME:
            g_value_set_string (
                value,
                itip_view_get_extension_name (
                ITIP_VIEW (object)));
            return;
    }

    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

static void
itip_view_dispose (GObject *object)
{
    ItipViewPrivate *priv;

    priv = ITIP_VIEW_GET_PRIVATE (object);

    if (priv->source_added_handler_id > 0) {
        g_signal_handler_disconnect (
            priv->registry,
            priv->source_added_handler_id);
        priv->source_added_handler_id = 0;
    }

    if (priv->source_removed_handler_id > 0) {
        g_signal_handler_disconnect (
            priv->registry,
            priv->source_removed_handler_id);
        priv->source_removed_handler_id = 0;
    }

    g_clear_object (&priv->client_cache);
    g_clear_object (&priv->registry);

    /* Chain up to parent's dispose() method. */
    G_OBJECT_CLASS (itip_view_parent_class)->dispose (object);
}

static void
itip_view_finalize (GObject *object)
{
    ItipViewPrivate *priv;
    GSList *iter;

    priv = ITIP_VIEW_GET_PRIVATE (object);

    d (printf ("Itip view finalized!\n"));

    g_clear_object (&priv->dom_document);
    g_free (priv->extension_name);
    g_free (priv->sender);
    g_free (priv->organizer);
    g_free (priv->organizer_sentby);
    g_free (priv->delegator);
    g_free (priv->attendee);
    g_free (priv->attendee_sentby);
    g_free (priv->proxy);
    g_free (priv->summary);
    g_free (priv->location);
    g_free (priv->status);
    g_free (priv->comment);
    g_free (priv->start_tm);
    g_free (priv->start_label);
    g_free (priv->end_tm);
    g_free (priv->end_label);
    g_free (priv->description);
    g_free (priv->error);

    for (iter = priv->lower_info_items; iter; iter = iter->next) {
        ItipViewInfoItem *item = iter->data;
        g_free (item->message);
        g_free (item);
    }

    g_slist_free (priv->lower_info_items);

    for (iter = priv->upper_info_items; iter; iter = iter->next) {
        ItipViewInfoItem *item = iter->data;
        g_free (item->message);
        g_free (item);
    }

    g_slist_free (priv->upper_info_items);

    /* Chain up to parent's finalize() method. */
    G_OBJECT_CLASS (itip_view_parent_class)->finalize (object);
}

static void
itip_view_constructed (GObject *object)
{
    ItipView *view;
    EClientCache *client_cache;
    ESourceRegistry *registry;
    gulong handler_id;

    view = ITIP_VIEW (object);
    client_cache = itip_view_get_client_cache (view);
    registry = e_client_cache_ref_registry (client_cache);

    /* Keep our own reference on the ESourceRegistry
     * to use when disconnecting these signal handlers. */
    view->priv->registry = g_object_ref (registry);

    handler_id = g_signal_connect (
        view->priv->registry, "source-added",
        G_CALLBACK (itip_view_source_added_cb), view);
    view->priv->source_added_handler_id = handler_id;

    handler_id = g_signal_connect (
        view->priv->registry, "source-removed",
        G_CALLBACK (itip_view_source_removed_cb), view);
    view->priv->source_removed_handler_id = handler_id;

    g_object_unref (registry);

    /* Chain up to parent's constructed() method. */
    G_OBJECT_CLASS (itip_view_parent_class)->constructed (object);
}

static void
itip_view_class_init (ItipViewClass *class)
{
    GObjectClass *object_class;

    g_type_class_add_private (class, sizeof (ItipViewPrivate));

    object_class = G_OBJECT_CLASS (class);
    object_class->set_property = itip_view_set_property;
    object_class->get_property = itip_view_get_property;
    object_class->dispose = itip_view_dispose;
    object_class->finalize = itip_view_finalize;
    object_class->constructed = itip_view_constructed;

    g_object_class_install_property (
        object_class,
        PROP_CLIENT_CACHE,
        g_param_spec_object (
            "client-cache",
            "Client Cache",
            "Cache of shared EClient instances",
            E_TYPE_CLIENT_CACHE,
            G_PARAM_READWRITE |
            G_PARAM_CONSTRUCT_ONLY));

    g_object_class_install_property (
        object_class,
        PROP_EXTENSION_NAME,
        g_param_spec_string (
            "extension-name",
            "Extension Name",
            "Show only data sources with this extension",
            NULL,
            G_PARAM_READWRITE));

    signals[SOURCE_SELECTED] = g_signal_new (
        "source_selected",
        G_TYPE_FROM_CLASS (class),
        G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (ItipViewClass, source_selected),
        NULL, NULL,
        g_cclosure_marshal_VOID__OBJECT,
        G_TYPE_NONE, 1,
        E_TYPE_SOURCE);

    signals[RESPONSE] = g_signal_new (
        "response",
        G_TYPE_FROM_CLASS (class),
        G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (ItipViewClass, response),
        NULL, NULL,
        g_cclosure_marshal_VOID__INT,
        G_TYPE_NONE, 1,
        G_TYPE_INT);
}

EMailPartItip *
itip_view_get_mail_part (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->itip_part;
}

EClientCache *
itip_view_get_client_cache (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->client_cache;
}

const gchar *
itip_view_get_extension_name (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->extension_name;
}

void
itip_view_set_extension_name (ItipView *view,
                              const gchar *extension_name)
{
    g_return_if_fail (ITIP_IS_VIEW (view));

    /* Avoid unnecessary rebuilds. */
    if (g_strcmp0 (extension_name, view->priv->extension_name) == 0)
        return;

    g_free (view->priv->extension_name);
    view->priv->extension_name = g_strdup (extension_name);

    g_object_notify (G_OBJECT (view), "extension-name");

    itip_view_rebuild_source_list (view);
}

void
itip_view_write (EMailFormatter *formatter,
                 GString *buffer)
{
    gchar *header = e_mail_formatter_get_html_header (formatter);
    g_string_append (buffer, header);
    g_free (header);

    g_string_append_printf (
        buffer,
        "<img src=\"gtk-stock://%s?size=%d\" class=\"itip icon\" />\n",
            MEETING_ICON, GTK_ICON_SIZE_BUTTON);

    g_string_append (
        buffer,
        "<div class=\"itip content\" id=\"" DIV_ITIP_CONTENT "\">\n");

        /* The first section listing the sender */
        /* FIXME What to do if the send and organizer do not match */
    g_string_append (
        buffer,
        "<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\"></div>\n");

    g_string_append (buffer, "<hr>\n");

        /* Elementary event information */
    g_string_append (
        buffer,
        "<table class=\"itip table\" border=\"0\" "
        "cellspacing=\"5\" cellpadding=\"0\">\n");

    append_text_table_row (buffer, TABLE_ROW_SUMMARY, NULL, NULL);
    append_text_table_row (buffer, TABLE_ROW_LOCATION, _("Location:"), NULL);
    append_text_table_row (buffer, TABLE_ROW_START_DATE, _("Start time:"), NULL);
    append_text_table_row (buffer, TABLE_ROW_END_DATE, _("End time:"), NULL);
    append_text_table_row (buffer, TABLE_ROW_STATUS, _("Status:"), NULL);
    append_text_table_row (buffer, TABLE_ROW_COMMENT, _("Comment:"), NULL);

    g_string_append (buffer, "</table>\n");

    /* Upper Info items */
    g_string_append (
        buffer,
        "<table class=\"itip info\" id=\"" TABLE_UPPER_ITIP_INFO "\" border=\"0\" "
        "cellspacing=\"5\" cellpadding=\"0\">");

        /* Description */
    g_string_append (
        buffer,
        "<div id=\"" TABLE_ROW_DESCRIPTION "\" class=\"itip description\" hidden=\"\"></div>\n");

    g_string_append (buffer, "<hr>\n");

    /* Lower Info items */
    g_string_append (
        buffer,
        "<table class=\"itip info\" id=\"" TABLE_LOWER_ITIP_INFO "\" border=\"0\" "
        "cellspacing=\"5\" cellpadding=\"0\">");

    g_string_append (
        buffer,
        "<table class=\"itip table\" border=\"0\" "
        "cellspacing=\"5\" cellpadding=\"0\">\n");

    g_string_append (
        buffer,
        "<tr id=\"" TABLE_ROW_ESCB "\" hidden=\"\""">"
        "<th><label id=\"" TABLE_ROW_ESCB_LABEL "\" for=\"" SELECT_ESOURCE "\"></label></th>"
        "<td><select name=\"" SELECT_ESOURCE "\" id=\"" SELECT_ESOURCE "\"></select></td>"
        "</tr>\n");

    /* RSVP area */
    append_checkbox_table_row (buffer, CHECKBOX_RSVP, _("Send reply to sender"));

        /* Comments */
    g_string_append_printf (
        buffer,
        "<tr id=\"" TABLE_ROW_RSVP_COMMENT "\" hidden=\"\">"
        "<th>%s</th>"
        "<td><textarea name=\"" TEXTAREA_RSVP_COMMENT "\" "
        "id=\"" TEXTAREA_RSVP_COMMENT "\" "
        "rows=\"3\" cols=\"40\" disabled=\"\">"
        "</textarea></td>\n"
        "</tr>\n",
        _("Comment:"));

        /* Updates */
    append_checkbox_table_row (buffer, CHECKBOX_UPDATE, _("Send _updates to attendees"));

        /* The recurrence check button */
    append_checkbox_table_row (buffer, CHECKBOX_RECUR, _("_Apply to all instances"));
    append_checkbox_table_row (buffer, CHECKBOX_FREE_TIME, _("Show time as _free"));
    append_checkbox_table_row (buffer, CHECKBOX_KEEP_ALARM, _("_Preserve my reminder"));
    append_checkbox_table_row (buffer, CHECKBOX_INHERIT_ALARM, _("_Inherit reminder"));

    g_string_append (buffer, "</table>\n");

        /* Buttons table */
    append_buttons_table (buffer);

        /* <div class="itip content" > */
    g_string_append (buffer, "</div>\n");

    g_string_append (buffer, "<div class=\"itip error\" id=\"" DIV_ITIP_ERROR "\"></div>");

    g_string_append (buffer, "</body></html>");
}

void
itip_view_write_for_printing (ItipView *view,
                              GString *buffer)
{
    if (view->priv->error && *view->priv->error) {
        g_string_append (buffer, view->priv->error);
        return;
    }

    g_string_append (
        buffer,
        "<div class=\"itip print_content\" id=\"" DIV_ITIP_CONTENT "\">\n");

        /* The first section listing the sender */
    if (view->priv->sender && *view->priv->sender) {
        /* FIXME What to do if the send and organizer do not match */
        g_string_append_printf (
            buffer,
            "<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\">%s</div>\n",
            view->priv->sender);

        g_string_append (buffer, "<hr>\n");
    }

        /* Elementary event information */
    g_string_append (
        buffer,
        "<table class=\"itip table\" border=\"0\" "
        "cellspacing=\"5\" cellpadding=\"0\">\n");

    append_text_table_row_nonempty (
        buffer, TABLE_ROW_SUMMARY,
        NULL, view->priv->summary);
    append_text_table_row_nonempty (
        buffer, TABLE_ROW_LOCATION,
        _("Location:"), view->priv->location);
    append_text_table_row_nonempty (
        buffer, TABLE_ROW_START_DATE,
        view->priv->start_header, view->priv->start_label);
    append_text_table_row_nonempty (
        buffer, TABLE_ROW_END_DATE,
        view->priv->end_header, view->priv->end_label);
    append_text_table_row_nonempty (
        buffer, TABLE_ROW_STATUS,
        _("Status:"), view->priv->status);
    append_text_table_row_nonempty (
        buffer, TABLE_ROW_COMMENT,
        _("Comment:"), view->priv->comment);

    g_string_append (buffer, "</table><br>\n");

        /* Description */
    if (view->priv->description && *view->priv->description) {
        g_string_append_printf (
            buffer,
            "<div id=\"" TABLE_ROW_DESCRIPTION "\" "
            "class=\"itip description\" %s>%s</div>\n",
            view->priv->description ? "" : "hidden=\"\"", view->priv->description);

        g_string_append (buffer, "</div>");
    }
}

void
itip_view_create_dom_bindings (ItipView *view,
                               WebKitDOMElement *element)
{
    WebKitDOMElement *el;
    WebKitDOMDocument *doc;

    doc = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
    view->priv->dom_document = g_object_ref (doc);

    el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RECUR);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (recur_toggled_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RSVP);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (rsvp_toggled_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_INHERIT_ALARM);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_KEEP_ALARM);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, BUTTON_OPEN_CALENDAR);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT_ALL);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE_ALL);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE_ALL);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE_ATTENDEE_STATUS);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, BUTTON_SEND_INFORMATION);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }

    el = webkit_dom_document_get_element_by_id (doc, SELECT_ESOURCE);
    if (el) {
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "change",
            G_CALLBACK (source_changed_cb), FALSE, view);
    }
}

static void
itip_view_init (ItipView *view)
{
    view->priv = ITIP_VIEW_GET_PRIVATE (view);

}

ItipView *
itip_view_new (EMailPartItip *puri,
               EClientCache *client_cache)
{
    ItipView *view;

    g_return_val_if_fail (E_IS_CLIENT_CACHE (client_cache), NULL);

    view = ITIP_VIEW (g_object_new (
        ITIP_TYPE_VIEW,
        "client-cache", client_cache,
        NULL));
    view->priv->itip_part = puri;

    return view;
}

static void
show_button (ItipView *view,
             const gchar *id)
{
    WebKitDOMElement *button;

    button = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, id);
    webkit_dom_html_element_set_hidden (
        WEBKIT_DOM_HTML_ELEMENT (button), FALSE);
}

void
itip_view_set_mode (ItipView *view,
                    ItipViewMode mode)
{
    WebKitDOMElement *row, *cell;
    WebKitDOMElement *button;

    g_return_if_fail (ITIP_IS_VIEW (view));

    view->priv->mode = mode;

    set_sender_text (view);

    if (!view->priv->dom_document)
        return;

    row = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TABLE_ROW_BUTTONS);
    cell = webkit_dom_element_get_first_element_child (row);
    do {
        button = webkit_dom_element_get_first_element_child (cell);
        webkit_dom_html_element_set_hidden (
            WEBKIT_DOM_HTML_ELEMENT (button), TRUE);
    } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);

    view->priv->is_recur_set = itip_view_get_recur_check_state (view);

        /* Always visible */
    show_button (view, BUTTON_OPEN_CALENDAR);

    switch (mode) {
    case ITIP_VIEW_MODE_PUBLISH:
        if (view->priv->needs_decline) {
            show_button (view, BUTTON_DECLINE);
        }
        show_button (view, BUTTON_ACCEPT);
        break;
    case ITIP_VIEW_MODE_REQUEST:
        show_button (view, view->priv->is_recur_set ? BUTTON_DECLINE_ALL : BUTTON_DECLINE);
        show_button (view, view->priv->is_recur_set ? BUTTON_TENTATIVE_ALL : BUTTON_TENTATIVE);
        show_button (view, view->priv->is_recur_set ? BUTTON_ACCEPT_ALL : BUTTON_ACCEPT);
        break;
    case ITIP_VIEW_MODE_ADD:
        if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
            show_button (view, BUTTON_DECLINE);
            show_button (view, BUTTON_TENTATIVE);
        }
        show_button (view, BUTTON_ACCEPT);
        break;
    case ITIP_VIEW_MODE_REFRESH:
        show_button (view, BUTTON_SEND_INFORMATION);
        break;
    case ITIP_VIEW_MODE_REPLY:
        show_button (view, BUTTON_UPDATE_ATTENDEE_STATUS);
        break;
    case ITIP_VIEW_MODE_CANCEL:
        show_button (view, BUTTON_UPDATE);
        break;
    case ITIP_VIEW_MODE_COUNTER:
    case ITIP_VIEW_MODE_DECLINECOUNTER:
        show_button (view, BUTTON_DECLINE);
        show_button (view, BUTTON_TENTATIVE);
        show_button (view, BUTTON_ACCEPT);
        break;
    default:
        break;
    }
}

ItipViewMode
itip_view_get_mode (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), ITIP_VIEW_MODE_NONE);

    return view->priv->mode;
}

void
itip_view_set_item_type (ItipView *view,
                         ECalClientSourceType type)
{
    WebKitDOMElement *label;
    const gchar *header;
    gchar *access_key, *html_label;

    g_return_if_fail (ITIP_IS_VIEW (view));

    view->priv->type = type;

    if (!view->priv->dom_document)
        return;

    label = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TABLE_ROW_ESCB_LABEL);

    switch (view->priv->type) {
        case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
            header = _("_Calendar:");
            break;
        case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
            header = _("_Tasks:");
            break;
        case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
            header = _("_Memos:");
            break;
        default:
            header = NULL;
            break;
    }

    if (!header) {
        set_sender_text (view);
        return;
    }

    html_label = e_mail_formatter_parse_html_mnemonics (header, &access_key);

    webkit_dom_html_element_set_access_key (
        WEBKIT_DOM_HTML_ELEMENT (label), access_key);
    webkit_dom_html_element_set_inner_html (
        WEBKIT_DOM_HTML_ELEMENT (label), html_label, NULL);

    g_free (html_label);

    if (access_key)
        g_free (access_key);

    set_sender_text (view);
}

ECalClientSourceType
itip_view_get_item_type (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), ITIP_VIEW_MODE_NONE);

    return view->priv->type;
}

void
itip_view_set_organizer (ItipView *view,
                         const gchar *organizer)
{
    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->organizer)
        g_free (view->priv->organizer);

    view->priv->organizer = e_utf8_ensure_valid (organizer);

    set_sender_text (view);
}

const gchar *
itip_view_get_organizer (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->organizer;
}

void
itip_view_set_organizer_sentby (ItipView *view,
                                const gchar *sentby)
{
    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->organizer_sentby)
        g_free (view->priv->organizer_sentby);

    view->priv->organizer_sentby = e_utf8_ensure_valid (sentby);

    set_sender_text (view);
}

const gchar *
itip_view_get_organizer_sentby (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->organizer_sentby;
}

void
itip_view_set_attendee (ItipView *view,
                        const gchar *attendee)
{
    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->attendee)
        g_free (view->priv->attendee);

    view->priv->attendee = e_utf8_ensure_valid (attendee);

    set_sender_text (view);
}

const gchar *
itip_view_get_attendee (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->attendee;
}

void
itip_view_set_attendee_sentby (ItipView *view,
                               const gchar *sentby)
{
    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->attendee_sentby)
        g_free (view->priv->attendee_sentby);

    view->priv->attendee_sentby = e_utf8_ensure_valid (sentby);

    set_sender_text (view);
}

const gchar *
itip_view_get_attendee_sentby (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->attendee_sentby;
}

void
itip_view_set_proxy (ItipView *view,
                     const gchar *proxy)
{
    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->proxy)
        g_free (view->priv->proxy);

    view->priv->proxy = e_utf8_ensure_valid (proxy);

    set_sender_text (view);
}

const gchar *
itip_view_get_proxy (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->proxy;
}

void
itip_view_set_delegator (ItipView *view,
                         const gchar *delegator)
{
    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->delegator)
        g_free (view->priv->delegator);

    view->priv->delegator = e_utf8_ensure_valid (delegator);

    set_sender_text (view);
}

const gchar *
itip_view_get_delegator (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->delegator;
}

void
itip_view_set_summary (ItipView *view,
                       const gchar *summary)
{
    WebKitDOMElement *row, *col;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->summary)
        g_free (view->priv->summary);

    view->priv->summary = summary ? g_strstrip (e_utf8_ensure_valid (summary)) : NULL;

    if (!view->priv->dom_document)
        return;

    row = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TABLE_ROW_SUMMARY);
    webkit_dom_html_element_set_hidden (
        WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->summary == NULL));

    col = webkit_dom_element_get_last_element_child (row);
    webkit_dom_html_element_set_inner_html (
        WEBKIT_DOM_HTML_ELEMENT (col),
        view->priv->summary ? view->priv->summary : "",
        NULL);
}

const gchar *
itip_view_get_summary (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->summary;
}

void
itip_view_set_location (ItipView *view,
                        const gchar *location)
{
    WebKitDOMElement *row, *col;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->location)
        g_free (view->priv->location);

    view->priv->location = location ? g_strstrip (e_utf8_ensure_valid (location)) : NULL;

    if (!view->priv->dom_document)
        return;

    row = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TABLE_ROW_LOCATION);
    webkit_dom_html_element_set_hidden (
        WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->location == NULL));

    col = webkit_dom_element_get_last_element_child (row);
    webkit_dom_html_element_set_inner_html (
        WEBKIT_DOM_HTML_ELEMENT (col),
        view->priv->location ? view->priv->location : "",
        NULL);
}

const gchar *
itip_view_get_location (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->location;
}

void
itip_view_set_status (ItipView *view,
                      const gchar *status)
{
    WebKitDOMElement *row, *col;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->status)
        g_free (view->priv->status);

    view->priv->status = status ? g_strstrip (e_utf8_ensure_valid (status)) : NULL;

    if (!view->priv->dom_document)
        return;

    row = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TABLE_ROW_STATUS);
    webkit_dom_html_element_set_hidden (
        WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->status == NULL));

    col = webkit_dom_element_get_last_element_child (row);
    webkit_dom_html_element_set_inner_html (
        WEBKIT_DOM_HTML_ELEMENT (col),
        view->priv->status ? view->priv->status : "",
        NULL);
}

const gchar *
itip_view_get_status (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->status;
}

void
itip_view_set_comment (ItipView *view,
                       const gchar *comment)
{
    WebKitDOMElement *row, *col;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->comment)
        g_free (view->priv->comment);

    view->priv->comment = comment ? g_strstrip (e_utf8_ensure_valid (comment)) : NULL;

    if (!view->priv->dom_document)
        return;

    row = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TABLE_ROW_COMMENT);
    webkit_dom_html_element_set_hidden (
        WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->comment == NULL));

    col = webkit_dom_element_get_last_element_child (row);
    webkit_dom_html_element_set_inner_html (
        WEBKIT_DOM_HTML_ELEMENT (col),
        view->priv->comment ? view->priv->comment : "",
        NULL);
}

const gchar *
itip_view_get_comment (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->comment;
}

void
itip_view_set_description (ItipView *view,
                           const gchar *description)
{
    WebKitDOMElement *div;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (view->priv->description)
        g_free (view->priv->description);

    view->priv->description = description ? g_strstrip (e_utf8_ensure_valid (description)) : NULL;

    if (!view->priv->dom_document)
        return;

    div = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TABLE_ROW_DESCRIPTION);
    webkit_dom_html_element_set_hidden (
        WEBKIT_DOM_HTML_ELEMENT (div), (view->priv->description == NULL));

    webkit_dom_html_element_set_inner_html (
        WEBKIT_DOM_HTML_ELEMENT (div),
        view->priv->description ? view->priv->description : "",
        NULL);
}

const gchar *
itip_view_get_description (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    return view->priv->description;
}

void
itip_view_set_start (ItipView *view,
                     struct tm *start,
                     gboolean is_date)
{
    ItipViewPrivate *priv;

    g_return_if_fail (ITIP_IS_VIEW (view));

    priv = view->priv;

    if (priv->start_tm && !start) {
        g_free (priv->start_tm);
        priv->start_tm = NULL;
    } else if (start) {
        if (!priv->start_tm)
            priv->start_tm = g_new0 (struct tm, 1);

        *priv->start_tm = *start;
    }

    priv->start_tm_is_date = is_date && start;

    update_start_end_times (view);
}

const struct tm *
itip_view_get_start (ItipView *view,
                     gboolean *is_date)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    if (is_date)
        *is_date = view->priv->start_tm_is_date;

    return view->priv->start_tm;
}

void
itip_view_set_end (ItipView *view,
                   struct tm *end,
                   gboolean is_date)
{
    ItipViewPrivate *priv;

    g_return_if_fail (ITIP_IS_VIEW (view));

    priv = view->priv;

    if (priv->end_tm && !end) {
        g_free (priv->end_tm);
        priv->end_tm = NULL;
    } else if (end) {
        if (!priv->end_tm)
            priv->end_tm = g_new0 (struct tm, 1);

        *priv->end_tm = *end;
    }

    priv->end_tm_is_date = is_date && end;

    update_start_end_times (view);
}

const struct tm *
itip_view_get_end (ItipView *view,
                   gboolean *is_date)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    if (is_date)
        *is_date = view->priv->end_tm_is_date;

    return view->priv->end_tm;
}

guint
itip_view_add_upper_info_item (ItipView *view,
                               ItipViewInfoItemType type,
                               const gchar *message)
{
    ItipViewPrivate *priv;
    ItipViewInfoItem *item;

    g_return_val_if_fail (ITIP_IS_VIEW (view), 0);

    priv = view->priv;

    item = g_new0 (ItipViewInfoItem, 1);

    item->type = type;
    item->message = e_utf8_ensure_valid (message);
    item->id = priv->next_info_item_id++;

    priv->upper_info_items = g_slist_append (priv->upper_info_items, item);

    if (!view->priv->dom_document)
        return item->id;

    append_info_item_row (view, TABLE_UPPER_ITIP_INFO, item);

    return item->id;
}

guint
itip_view_add_upper_info_item_printf (ItipView *view,
                                      ItipViewInfoItemType type,
                                      const gchar *format,
                                      ...)
{
    va_list args;
    gchar *message;
    guint id;

    g_return_val_if_fail (ITIP_IS_VIEW (view), 0);

    va_start (args, format);
    message = g_strdup_vprintf (format, args);
    va_end (args);

    id = itip_view_add_upper_info_item (view, type, message);
    g_free (message);

    return id;
}

void
itip_view_remove_upper_info_item (ItipView *view,
                                  guint id)
{
    ItipViewPrivate *priv;
    GSList *l;

    g_return_if_fail (ITIP_IS_VIEW (view));

    priv = view->priv;

    for (l = priv->upper_info_items; l; l = l->next) {
        ItipViewInfoItem *item = l->data;

        if (item->id == id) {
            priv->upper_info_items = g_slist_remove (priv->upper_info_items, item);

            g_free (item->message);
            g_free (item);

            if (!view->priv->dom_document)
                remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, id);

            return;
        }
    }
}

void
itip_view_clear_upper_info_items (ItipView *view)
{
    ItipViewPrivate *priv;
    GSList *l;

    g_return_if_fail (ITIP_IS_VIEW (view));

    priv = view->priv;

    for (l = priv->upper_info_items; l; l = l->next) {
        ItipViewInfoItem *item = l->data;

        if (view->priv->dom_document)
            remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, item->id);

        g_free (item->message);
        g_free (item);
    }

    g_slist_free (priv->upper_info_items);
    priv->upper_info_items = NULL;
}

guint
itip_view_add_lower_info_item (ItipView *view,
                               ItipViewInfoItemType type,
                               const gchar *message)
{
    ItipViewPrivate *priv;
    ItipViewInfoItem *item;

    g_return_val_if_fail (ITIP_IS_VIEW (view), 0);

    priv = view->priv;

    item = g_new0 (ItipViewInfoItem, 1);

    item->type = type;
    item->message = e_utf8_ensure_valid (message);
    item->id = priv->next_info_item_id++;

    priv->lower_info_items = g_slist_append (priv->lower_info_items, item);

    if (!view->priv->dom_document)
        return item->id;

    append_info_item_row (view, TABLE_LOWER_ITIP_INFO, item);

    return item->id;
}

guint
itip_view_add_lower_info_item_printf (ItipView *view,
                                      ItipViewInfoItemType type,
                                      const gchar *format,
                                      ...)
{
    va_list args;
    gchar *message;
    guint id;

    g_return_val_if_fail (ITIP_IS_VIEW (view), 0);

    va_start (args, format);
    message = g_strdup_vprintf (format, args);
    va_end (args);

    id = itip_view_add_lower_info_item (view, type, message);
    g_free (message);

    return id;
}

void
itip_view_remove_lower_info_item (ItipView *view,
                                  guint id)
{
    ItipViewPrivate *priv;
    GSList *l;

    g_return_if_fail (ITIP_IS_VIEW (view));

    priv = view->priv;

    for (l = priv->lower_info_items; l; l = l->next) {
        ItipViewInfoItem *item = l->data;

        if (item->id == id) {
            priv->lower_info_items = g_slist_remove (priv->lower_info_items, item);

            g_free (item->message);
            g_free (item);

            if (view->priv->dom_document)
                remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, id);

            return;
        }
    }
}

void
itip_view_clear_lower_info_items (ItipView *view)
{
    ItipViewPrivate *priv;
    GSList *l;

    g_return_if_fail (ITIP_IS_VIEW (view));

    priv = view->priv;

    for (l = priv->lower_info_items; l; l = l->next) {
        ItipViewInfoItem *item = l->data;

        if (view->priv->dom_document)
            remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, item->id);

        g_free (item->message);
        g_free (item);
    }

    g_slist_free (priv->lower_info_items);
    priv->lower_info_items = NULL;
}

void
itip_view_set_source (ItipView *view,
                      ESource *source)
{
    WebKitDOMElement *select;
    WebKitDOMElement *row;
    ESource *selected_source;
    gulong i, len;

    g_return_if_fail (ITIP_IS_VIEW (view));

    d (printf ("Settings default source '%s'\n", e_source_get_display_name (source)));

    if (!view->priv->dom_document)
        return;

    row = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TABLE_ROW_ESCB);
    webkit_dom_html_element_set_hidden (
        WEBKIT_DOM_HTML_ELEMENT (row), (source == NULL));
    if (source == NULL)
        return;

    select = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, SELECT_ESOURCE);

        /* <select> does not emit 'change' event when already selected
     * <option> is re-selected, but we need to notify itip formatter,
     * so that it would make all the buttons sensitive */
    selected_source = itip_view_ref_source (view);
    if (source == selected_source) {
        source_changed_cb (select, NULL, view);
        return;
    }

    if (selected_source != NULL)
        g_object_unref (selected_source);

    if (webkit_dom_html_select_element_get_disabled (
            WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
        webkit_dom_html_select_element_set_disabled (
            WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
    }

    len = webkit_dom_html_select_element_get_length (
        WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
    for (i = 0; i < len; i++) {

        WebKitDOMNode *node;
        WebKitDOMHTMLOptionElement *option;
        gchar *value;

        node = webkit_dom_html_select_element_item (
            WEBKIT_DOM_HTML_SELECT_ELEMENT (select), i);
        option = WEBKIT_DOM_HTML_OPTION_ELEMENT (node);

        value = webkit_dom_html_option_element_get_value (option);
        if (g_strcmp0 (value, e_source_get_uid (source)) == 0) {
            webkit_dom_html_option_element_set_selected (
                option, TRUE);

            g_free (value);
            break;
        }

        g_free (value);
    }

    source_changed_cb (select, NULL, view);
}

ESource *
itip_view_ref_source (ItipView *view)
{
    WebKitDOMElement *select;
    gchar *uid;
    ESource *source;
    gboolean disable = FALSE;

    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    if (!view->priv->dom_document)
        return NULL;

    select = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, SELECT_ESOURCE);
    if (webkit_dom_html_select_element_get_disabled (
            WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
        webkit_dom_html_select_element_set_disabled (
        WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
        disable = TRUE;
    }

    uid = webkit_dom_html_select_element_get_value (
        WEBKIT_DOM_HTML_SELECT_ELEMENT (select));

    source = e_source_registry_ref_source (view->priv->registry, uid);

    g_free (uid);

    if (disable) {
        webkit_dom_html_select_element_set_disabled (
            WEBKIT_DOM_HTML_SELECT_ELEMENT (select), TRUE);
    }

    return source;
}

void
itip_view_set_rsvp (ItipView *view,
                    gboolean rsvp)
{
    WebKitDOMElement *el;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (!view->priv->dom_document)
        return;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_RSVP);
    webkit_dom_html_input_element_set_checked (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el), rsvp);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
    webkit_dom_html_text_area_element_set_disabled (
        WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
}

gboolean
itip_view_get_rsvp (ItipView *view)
{
    WebKitDOMElement *el;

    g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);

    if (!view->priv->dom_document)
        return FALSE;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_RSVP);
    return webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
}

void
itip_view_set_show_rsvp_check (ItipView *view,
                               gboolean show)
{
    WebKitDOMElement *label;
    WebKitDOMElement *el;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (!view->priv->dom_document)
        return;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, "table_row_" CHECKBOX_RSVP);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_RSVP);
    label = webkit_dom_element_get_next_element_sibling (el);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);

    if (!show) {
        webkit_dom_html_input_element_set_checked (
            WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
    }

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TABLE_ROW_RSVP_COMMENT);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
}

gboolean
itip_view_get_show_rsvp_check (ItipView *view)
{
    WebKitDOMElement *el;

    g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);

    if (!view->priv->dom_document)
        return FALSE;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_RSVP);
    return !webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
}

void
itip_view_set_update (ItipView *view,
                      gboolean update)
{
    WebKitDOMElement *el;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (!view->priv->dom_document)
        return;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_UPDATE);

    webkit_dom_html_input_element_set_checked (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el), update);
}

gboolean
itip_view_get_update (ItipView *view)
{
    WebKitDOMElement *el;

    g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);

    if (!view->priv->dom_document)
        return FALSE;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_UPDATE);
    return webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
}

void
itip_view_set_show_update_check (ItipView *view,
                                 gboolean show)
{
    WebKitDOMElement *label;
    WebKitDOMElement *el;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (!view->priv->dom_document)
        return;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, "table_row_" CHECKBOX_UPDATE);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_UPDATE);
    label = webkit_dom_element_get_next_element_sibling (el);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);

    if (!show) {
        webkit_dom_html_input_element_set_checked (
            WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
    }
}

gboolean
itip_view_get_show_update_check (ItipView *view)
{
    WebKitDOMElement *el;

    g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);

    if (!view->priv->dom_document)
        return FALSE;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_UPDATE);
    return !webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
}

void
itip_view_set_rsvp_comment (ItipView *view,
                            const gchar *comment)
{
    WebKitDOMElement *el;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (!view->priv->dom_document)
        return;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
    webkit_dom_html_element_set_hidden (
        WEBKIT_DOM_HTML_ELEMENT (el), (comment == NULL));

    if (comment) {
        webkit_dom_html_text_area_element_set_value (
            WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), comment);
    }
}

gchar *
itip_view_get_rsvp_comment (ItipView *view)
{
    WebKitDOMElement *el;

    g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);

    if (!view->priv->dom_document)
        return NULL;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TEXTAREA_RSVP_COMMENT);

    if (webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el))) {
        return NULL;
    }

    return webkit_dom_html_text_area_element_get_value (
        WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el));
}

void
itip_view_set_needs_decline (ItipView *view,
                             gboolean needs_decline)
{
    g_return_if_fail (ITIP_IS_VIEW (view));

    view->priv->needs_decline = needs_decline;
}

void
itip_view_set_buttons_sensitive (ItipView *view,
                                 gboolean sensitive)
{
    WebKitDOMElement *el, *cell;

    g_return_if_fail (ITIP_IS_VIEW (view));

    d (printf ("Settings buttons %s\n", sensitive ? "sensitive" : "insensitive"));

    view->priv->buttons_sensitive = sensitive;

    if (!view->priv->dom_document)
        return;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_UPDATE);
    webkit_dom_html_input_element_set_disabled (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_RECUR);
    webkit_dom_html_input_element_set_disabled (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_FREE_TIME);
    webkit_dom_html_input_element_set_disabled (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_KEEP_ALARM);
    webkit_dom_html_input_element_set_disabled (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
    webkit_dom_html_input_element_set_disabled (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_RSVP);
    webkit_dom_html_input_element_set_disabled (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
    webkit_dom_html_text_area_element_set_disabled (
        WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !sensitive);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, TABLE_ROW_BUTTONS);
    cell = webkit_dom_element_get_first_element_child (el);
    do {
        WebKitDOMElement *btn;
        btn = webkit_dom_element_get_first_element_child (cell);
        if (!webkit_dom_html_element_get_hidden (
            WEBKIT_DOM_HTML_ELEMENT (btn))) {
            webkit_dom_html_button_element_set_disabled (
                WEBKIT_DOM_HTML_BUTTON_ELEMENT (btn), !sensitive);
        }
    } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
}

gboolean
itip_view_get_buttons_sensitive (ItipView *view)
{
    g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);

    return view->priv->buttons_sensitive;
}

gboolean
itip_view_get_recur_check_state (ItipView *view)
{
    WebKitDOMElement *el;

    g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);

    if (!view->priv->dom_document)
        return FALSE;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_RECUR);
    return webkit_dom_html_input_element_get_checked (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
}

void
itip_view_set_show_recur_check (ItipView *view,
                                gboolean show)
{
    WebKitDOMElement *label;
    WebKitDOMElement *el;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (!view->priv->dom_document)
        return;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, "table_row_" CHECKBOX_RECUR);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_RECUR);
    label = webkit_dom_element_get_next_element_sibling (el);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);

    if (!show) {
        webkit_dom_html_input_element_set_checked (
            WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
    }

        /* and update state of the second check */
    alarm_check_toggled_cb (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
        NULL, view);
}

void
itip_view_set_show_free_time_check (ItipView *view,
                                    gboolean show)
{
    WebKitDOMElement *label;
    WebKitDOMElement *el;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (!view->priv->dom_document)
        return;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, "table_row_" CHECKBOX_FREE_TIME);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_FREE_TIME);
    label = webkit_dom_element_get_next_element_sibling (el);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);

    if (!show) {
        webkit_dom_html_input_element_set_checked (
            WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
    }

        /* and update state of the second check */
    alarm_check_toggled_cb (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
        NULL, view);
}

gboolean
itip_view_get_free_time_check_state (ItipView *view)
{
    WebKitDOMElement *el;

    g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);

    if (!view->priv->dom_document)
        return FALSE;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_FREE_TIME);
    return webkit_dom_html_input_element_get_checked (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
}

void
itip_view_set_show_keep_alarm_check (ItipView *view,
                                     gboolean show)
{
    WebKitDOMElement *label;
    WebKitDOMElement *el;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (!view->priv->dom_document)
        return;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, "table_row_" CHECKBOX_KEEP_ALARM);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_KEEP_ALARM);
    label = webkit_dom_element_get_next_element_sibling (el);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);

    if (!show) {
        webkit_dom_html_input_element_set_checked (
            WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
    }

        /* and update state of the second check */
    alarm_check_toggled_cb (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
        NULL, view);
}

gboolean
itip_view_get_keep_alarm_check_state (ItipView *view)
{
    WebKitDOMElement *el;

    g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);

    if (!view->priv->dom_document)
        return FALSE;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_KEEP_ALARM);
    return webkit_dom_html_input_element_get_checked (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
}

void
itip_view_set_show_inherit_alarm_check (ItipView *view,
                                        gboolean show)
{
    WebKitDOMElement *label;
    WebKitDOMElement *el;

    g_return_if_fail (ITIP_IS_VIEW (view));

    if (!view->priv->dom_document)
        return;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, "table_row_" CHECKBOX_INHERIT_ALARM);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
    label = webkit_dom_element_get_next_element_sibling (el);
    webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);

    if (!show) {
        webkit_dom_html_input_element_set_checked (
            WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
    }

    /* and update state of the second check */
    alarm_check_toggled_cb (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
        NULL, view);
}

gboolean
itip_view_get_inherit_alarm_check_state (ItipView *view)
{
    WebKitDOMElement *el;

    g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);

    if (!view->priv->dom_document)
        return FALSE;

    el = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
    return webkit_dom_html_input_element_get_checked (
        WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
}

void
itip_view_set_error (ItipView *view,
                     const gchar *error_html,
                     gboolean show_save_btn)
{
    WebKitDOMElement *content, *error;
    GString *str;

    g_return_if_fail (ITIP_IS_VIEW (view));
    g_return_if_fail (error_html);

    str = g_string_new (error_html);

    if (show_save_btn) {
        g_string_append (
            str,
            "<table border=\"0\" width=\"100%\">"
            "<tr width=\"100%\" id=\"" TABLE_ROW_BUTTONS "\">");

        buttons_table_write_button (
            str, BUTTON_SAVE, _("Sa_ve"),
            GTK_STOCK_SAVE, ITIP_VIEW_RESPONSE_SAVE);

        g_string_append (str, "</tr></table>");
    }

    view->priv->error = str->str;
    g_string_free (str, FALSE);

    if (!view->priv->dom_document)
        return;

    content = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, DIV_ITIP_CONTENT);
    webkit_dom_html_element_set_hidden (
        WEBKIT_DOM_HTML_ELEMENT (content), TRUE);

    error = webkit_dom_document_get_element_by_id (
        view->priv->dom_document, DIV_ITIP_ERROR);
    webkit_dom_html_element_set_hidden (
        WEBKIT_DOM_HTML_ELEMENT (error), FALSE);

    webkit_dom_html_element_set_inner_html (
        WEBKIT_DOM_HTML_ELEMENT (error), view->priv->error, NULL);

    if (show_save_btn) {
        WebKitDOMElement *el;

        show_button (view, BUTTON_SAVE);

        el = webkit_dom_document_get_element_by_id (
            view->priv->dom_document, BUTTON_SAVE);
        webkit_dom_html_button_element_set_disabled (
            WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE);
        webkit_dom_event_target_add_event_listener (
            WEBKIT_DOM_EVENT_TARGET (el), "click",
            G_CALLBACK (button_clicked_cb), FALSE, view);
    }
}

/******************************************************************************/

typedef struct {
    EMailPartItip *puri;
        ItipView *view;
    GCancellable *itip_cancellable;
    GCancellable *cancellable;
    gulong cancelled_id;
    gboolean keep_alarm_check;
    GHashTable *conflicts;

    gchar *uid;
    gchar *rid;

    gchar *sexp;

    gint count;
} FormatItipFindData;

static gboolean check_is_instance (icalcomponent *icalcomp);

static icalproperty *
find_attendee (icalcomponent *ical_comp,
               const gchar *address)
{
    icalproperty *prop;

    if (address == NULL)
        return NULL;

    for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
         prop != NULL;
         prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
        gchar *attendee;
        gchar *text;

        attendee = icalproperty_get_value_as_string_r (prop);

         if (!attendee)
            continue;

        text = g_strdup (itip_strip_mailto (attendee));
        text = g_strstrip (text);
        if (text && !g_ascii_strcasecmp (address, text)) {
            g_free (text);
            g_free (attendee);
            break;
        }
        g_free (text);
        g_free (attendee);
    }

    return prop;
}

static icalproperty *
find_attendee_if_sentby (icalcomponent *ical_comp,
                         const gchar *address)
{
    icalproperty *prop;

    if (address == NULL)
        return NULL;

    for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
         prop != NULL;
         prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
        icalparameter *param;
        const gchar *attendee_sentby;
        gchar *text;

        param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
        if (!param)
            continue;

        attendee_sentby = icalparameter_get_sentby (param);

        if (!attendee_sentby)
            continue;

        text = g_strdup (itip_strip_mailto (attendee_sentby));
        text = g_strstrip (text);
        if (text && !g_ascii_strcasecmp (address, text)) {
            g_free (text);
            break;
        }
        g_free (text);
    }

    return prop;
}

static void
find_to_address (ItipView *view,
                 EMailPartItip *itip_part,
                 icalcomponent *ical_comp,
                 icalparameter_partstat *status)
{
    ESourceRegistry *registry;
    ESourceMailIdentity *extension;
    GList *list, *link;
    const gchar *extension_name;

    registry = view->priv->registry;
    extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;

    if (itip_part->to_address != NULL)
        return;

    if (itip_part->msg != NULL && itip_part->folder != NULL) {
        ESource *source;

        source = em_utils_guess_mail_identity (
            registry, itip_part->msg,
            itip_part->folder, itip_part->uid);

        if (source != NULL) {
            extension = e_source_get_extension (source, extension_name);

            itip_part->to_address = e_source_mail_identity_dup_address (extension);

            g_object_unref (source);
        }
    }

    if (itip_part->to_address != NULL)
        return;

    /* Look through the list of attendees to find the user's address */
    list = e_source_registry_list_enabled (registry, extension_name);

    for (link = list; link != NULL; link = g_list_next (link)) {
        ESource *source = E_SOURCE (link->data);
        icalproperty *prop = NULL;
        icalparameter *param;
        const gchar *address;
        gchar *text;

        extension = e_source_get_extension (source, extension_name);
        address = e_source_mail_identity_get_address (extension);

        prop = find_attendee (ical_comp, address);
        if (prop == NULL)
            continue;

        param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
        if (param != NULL)
            itip_part->to_name = g_strdup (icalparameter_get_cn (param));

        text = icalproperty_get_value_as_string_r (prop);

        itip_part->to_address = g_strdup (itip_strip_mailto (text));
        g_free (text);
        g_strstrip (itip_part->to_address);

        itip_part->my_address = g_strdup (address);

        param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
        if (param != NULL &&
            icalparameter_get_rsvp (param) == ICAL_RSVP_FALSE)
            itip_part->no_reply_wanted = TRUE;

        if (status) {
            param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
            *status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
        }

        break;
    }

    g_list_free_full (list, (GDestroyNotify) g_object_unref);

    if (itip_part->to_address != NULL)
        return;

    /* If the user's address was not found in the attendee's list,
     * then the user might be responding on behalf of his/her delegator.
     * In this case, we would want to go through the SENT-BY fields of
     * the attendees to find the user's address.
     *
 *
     * Note: This functionality could have been (easily) implemented
     * in the previous loop, but it would hurt the performance for all
     * providers in general. Hence, we choose to iterate through the
     * accounts list again.
     */

    list = e_source_registry_list_enabled (registry, extension_name);

    for (link = list; link != NULL; link = g_list_next (link)) {
        ESource *source = E_SOURCE (link->data);
        icalproperty *prop = NULL;
        icalparameter *param;
        const gchar *address;
        gchar *text;

        extension = e_source_get_extension (source, extension_name);
        address = e_source_mail_identity_get_address (extension);

        prop = find_attendee_if_sentby (ical_comp, address);
        if (prop == NULL)
            continue;

        param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
        if (param != NULL)
            itip_part->to_name = g_strdup (icalparameter_get_cn (param));

        text = icalproperty_get_value_as_string_r (prop);

        itip_part->to_address = g_strdup (itip_strip_mailto (text));
        g_free (text);
        g_strstrip (itip_part->to_address);

        itip_part->my_address = g_strdup (address);

        param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
        if (param != NULL &&
            ICAL_RSVP_FALSE == icalparameter_get_rsvp (param))
            itip_part->no_reply_wanted = TRUE;

        if (status) {
            param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
            *status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
        }

        break;
    }

    g_list_free_full (list, (GDestroyNotify) g_object_unref);
}

static void
find_from_address (ItipView *view,
                   EMailPartItip *pitip,
                   icalcomponent *ical_comp)
{
    ESourceRegistry *registry;
    GList *list, *link;
    icalproperty *prop;
    gchar *organizer;
    icalparameter *param;
    const gchar *extension_name;
    const gchar *organizer_sentby;
    gchar *organizer_clean = NULL;
    gchar *organizer_sentby_clean = NULL;

    registry = view->priv->registry;

    prop = icalcomponent_get_first_property (ical_comp, ICAL_ORGANIZER_PROPERTY);

    if (!prop)
        return;

    organizer = icalproperty_get_value_as_string_r (prop);
    if (organizer) {
        organizer_clean = g_strdup (itip_strip_mailto (organizer));
        organizer_clean = g_strstrip (organizer_clean);
        g_free (organizer);
    }

    param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
    if (param) {
        organizer_sentby = icalparameter_get_sentby (param);
        if (organizer_sentby) {
            organizer_sentby_clean = g_strdup (itip_strip_mailto (organizer_sentby));
            organizer_sentby_clean = g_strstrip (organizer_sentby_clean);
        }
    }

    if (!(organizer_sentby_clean || organizer_clean))
        return;

    pitip->from_address = g_strdup (organizer_clean);

    param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
    if (param)
        pitip->from_name = g_strdup (icalparameter_get_cn (param));

    extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
    list = e_source_registry_list_enabled (registry, extension_name);

    for (link = list; link != NULL; link = g_list_next (link)) {
        ESource *source = E_SOURCE (link->data);
        ESourceMailIdentity *extension;
        const gchar *address;

        extension = e_source_get_extension (source, extension_name);
        address = e_source_mail_identity_get_address (extension);

        if (address == NULL)
            continue;

        if ((organizer_clean && !g_ascii_strcasecmp (organizer_clean, address))
            || (organizer_sentby_clean && !g_ascii_strcasecmp (organizer_sentby_clean, address))) {
            pitip->my_address = g_strdup (address);

            break;
        }
    }

    g_list_free_full (list, (GDestroyNotify) g_object_unref);

    g_free (organizer_sentby_clean);
    g_free (organizer_clean);
}

static ECalComponent *
get_real_item (EMailPartItip *pitip)
{
    ECalComponent *comp = NULL;
    ESource *source;

    source = e_client_get_source (E_CLIENT (pitip->current_client));
    if (source)
        comp = g_hash_table_lookup (pitip->real_comps, e_source_get_uid (source));

    if (!comp) {
        return NULL;
    }

    return e_cal_component_clone (comp);
}

static void
adjust_item (EMailPartItip *pitip,
             ECalComponent *comp)
{
    ECalComponent *real_comp;

    real_comp = get_real_item (pitip);
    if (real_comp != NULL) {
        ECalComponentText text;
        const gchar *string;
        GSList *l;

        e_cal_component_get_summary (real_comp, &text);
        e_cal_component_set_summary (comp, &text);
        e_cal_component_get_location (real_comp, &string);
        e_cal_component_set_location (comp, string);
        e_cal_component_get_description_list (real_comp, &l);
        e_cal_component_set_description_list (comp, l);
        e_cal_component_free_text_list (l);

        g_object_unref (real_comp);
    } else {
        ECalComponentText text = {_("Unknown"), NULL};

        e_cal_component_set_summary (comp, &text);
    }
}

static void
set_buttons_sensitive (EMailPartItip *pitip,
                       ItipView *view)
{
    gboolean read_only = TRUE;

    if (pitip->current_client)
        read_only = e_client_is_readonly (E_CLIENT (pitip->current_client));

    itip_view_set_buttons_sensitive (view, pitip->current_client != NULL && !read_only);
}

static void
add_failed_to_load_msg (ItipView *view,
                        const GError *error)
{
    g_return_if_fail (view != NULL);
    g_return_if_fail (error != NULL);

    itip_view_add_lower_info_item (
        view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING, error->message);
}

static void
itip_view_cal_opened_cb (GObject *source_object,
                         GAsyncResult *result,
                         gpointer user_data)
{
    ItipView *view;
    EMailPartItip *pitip;
    EClient *client;
    GError *error = NULL;

    view = ITIP_VIEW (user_data);
    pitip = itip_view_get_mail_part (view);

    client = e_client_cache_get_client_finish (
        E_CLIENT_CACHE (source_object), result, &error);

    /* Sanity check. */
    g_return_if_fail (
        ((client != NULL) && (error == NULL)) ||
        ((client == NULL) && (error != NULL)));

    /* Ignore cancellations. */
    if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
        g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
        g_error_free (error);
        goto exit;

    } else if (error != NULL) {
        add_failed_to_load_msg (view, error);
        g_error_free (error);
        goto exit;
    }

    if (e_cal_client_check_recurrences_no_master (E_CAL_CLIENT (client))) {
        icalcomponent *icalcomp;
        gboolean show_recur_check;

        icalcomp = e_cal_component_get_icalcomponent (pitip->comp);

        show_recur_check = check_is_instance (icalcomp);
        itip_view_set_show_recur_check (view, show_recur_check);
    }

    if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
        gboolean needs_decline;

        needs_decline = e_client_check_capability (
            client,
            CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING);
        itip_view_set_needs_decline (view, needs_decline);
        itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
    }

    pitip->current_client = g_object_ref (client);

    set_buttons_sensitive (pitip, view);

exit:
    g_clear_object (&client);
    g_clear_object (&view);
}

static void
start_calendar_server (EMailPartItip *pitip,
                       ItipView *view,
                       ESource *source,
                       ECalClientSourceType type,
                       GAsyncReadyCallback func,
                       gpointer data)
{
    EClientCache *client_cache;
    const gchar *extension_name = NULL;

    g_return_if_fail (source != NULL);

    switch (type) {
        case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
            extension_name = E_SOURCE_EXTENSION_CALENDAR;
            break;
        case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
            extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
            break;
        case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
            extension_name = E_SOURCE_EXTENSION_TASK_LIST;
            break;
        default:
            g_return_if_reached ();
    }

    client_cache = itip_view_get_client_cache (view);

    e_client_cache_get_client (
        client_cache, source, extension_name,
        pitip->cancellable, func, data);
}

static void
start_calendar_server_by_uid (EMailPartItip *pitip,
                              ItipView *view,
                              const gchar *uid,
                              ECalClientSourceType type)
{
    ESource *source;

    itip_view_set_buttons_sensitive (view, FALSE);

    source = e_source_registry_ref_source (view->priv->registry, uid);

    if (source != NULL) {
        start_calendar_server (
            pitip, view, source, type,
            itip_view_cal_opened_cb,
            g_object_ref (view));
        g_object_unref (source);
    }
}

static void
source_selected_cb (ItipView *view,
                    ESource *source,
                    gpointer data)
{
    EMailPartItip *pitip = data;

    itip_view_set_buttons_sensitive (view, FALSE);

    g_return_if_fail (source != NULL);

    start_calendar_server (
        pitip, view, source, pitip->type,
        itip_view_cal_opened_cb,
        g_object_ref (view));
}

static void
find_cal_update_ui (FormatItipFindData *fd,
                    ECalClient *cal_client)
{
    EMailPartItip *pitip;
    ItipView *view;
    ESource *source;

    g_return_if_fail (fd != NULL);

    pitip = fd->puri;
    view = fd->view;

    /* UI part gone */
    if (g_cancellable_is_cancelled (fd->cancellable))
        return;

    source = cal_client ? e_client_get_source (E_CLIENT (cal_client)) : NULL;

    if (cal_client && g_hash_table_lookup (fd->conflicts, cal_client)) {
        itip_view_add_upper_info_item_printf (
            view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
            _("An appointment in the calendar "
            "'%s' conflicts with this meeting"),
            e_source_get_display_name (source));
    }

    /* search for a master object if the detached object doesn't exist in the calendar */
    if (pitip->current_client && pitip->current_client == cal_client) {
        gboolean rsvp_enabled = FALSE;

        itip_view_set_show_keep_alarm_check (view, fd->keep_alarm_check);

        pitip->current_client = cal_client;

        /* Provide extra info, since its not in the component */
        /* FIXME Check sequence number of meeting? */
        /* FIXME Do we need to adjust elsewhere for the delegated calendar item? */
        /* FIXME Need to update the fields in the view now */
        if (pitip->method == ICAL_METHOD_REPLY || pitip->method == ICAL_METHOD_REFRESH)
            adjust_item (pitip, pitip->comp);

        /* We clear everything because we don't really care
         * about any other info/warnings now we found an
         * existing versions */
        itip_view_clear_lower_info_items (view);
        pitip->progress_info_id = 0;

        /* FIXME Check read only state of calendar? */
        itip_view_add_lower_info_item_printf (
            view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
            _("Found the appointment in the calendar '%s'"), e_source_get_display_name (source));

        /*
         * Only allow replies if backend doesn't do that automatically.
         * Only enable it for forwarded invitiations (PUBLISH) or direct
         * invitiations (REQUEST), but not replies (REPLY).
         * Replies only make sense for events with an organizer.
         */
        if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
            (pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST) &&
            pitip->has_organizer) {
            rsvp_enabled = TRUE;
        }
        itip_view_set_show_rsvp_check (view, rsvp_enabled);

        /* default is chosen in extract_itip_data() based on content of the VEVENT */
        itip_view_set_rsvp (view, !pitip->no_reply_wanted);

        set_buttons_sensitive (pitip, view);

        g_cancellable_cancel (fd->cancellable);
    } else if (!pitip->current_client)
        itip_view_set_show_keep_alarm_check (view, FALSE);

    if (pitip->current_client && pitip->current_client == cal_client) {
        if (e_cal_client_check_recurrences_no_master (pitip->current_client)) {
            icalcomponent *icalcomp = e_cal_component_get_icalcomponent (pitip->comp);

            if (check_is_instance (icalcomp))
                itip_view_set_show_recur_check (view, TRUE);
            else
                itip_view_set_show_recur_check (view, FALSE);
        }

        if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
            /* TODO The static capability should be made generic to convey that the calendar contains unaccepted items */
            if (e_client_check_capability (E_CLIENT (pitip->current_client), CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING))
                itip_view_set_needs_decline (view, TRUE);
            else
                itip_view_set_needs_decline (view, FALSE);

            itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
        }
    }
}

static void
decrease_find_data (FormatItipFindData *fd)
{
    g_return_if_fail (fd != NULL);

    fd->count--;
    d (printf ("Decreasing itip formatter search count to %d\n", fd->count));

    if (fd->count == 0 && !g_cancellable_is_cancelled (fd->cancellable)) {
        gboolean rsvp_enabled = FALSE;
        EMailPartItip *pitip = fd->puri;
        ItipView *view = fd->view;

        itip_view_remove_lower_info_item (view, pitip->progress_info_id);
        pitip->progress_info_id = 0;

        /*
         * Only allow replies if backend doesn't do that automatically.
         * Only enable it for forwarded invitiations (PUBLISH) or direct
         * invitiations (REQUEST), but not replies (REPLY).
         * Replies only make sense for events with an organizer.
         */
        if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
            (pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST) &&
            pitip->has_organizer) {
            rsvp_enabled = TRUE;
        }
        itip_view_set_show_rsvp_check (view, rsvp_enabled);

        /* default is chosen in extract_itip_data() based on content of the VEVENT */
        itip_view_set_rsvp (view, !pitip->no_reply_wanted);

        if ((pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST)
            && !pitip->current_client) {
            /* Reuse already declared one or rename? */
            ESource *source = NULL;
            const gchar *extension_name;

            switch (pitip->type) {
                case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                    extension_name = E_SOURCE_EXTENSION_CALENDAR;
                    break;
                case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
                    extension_name = E_SOURCE_EXTENSION_TASK_LIST;
                    break;
                case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
                    extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
                    break;
                default:
                    g_return_if_reached ();
            }

            source = e_source_registry_ref_default_for_extension_name (
                view->priv->registry, extension_name);

            itip_view_set_extension_name (view, extension_name);

            g_signal_connect (
                view, "source_selected",
                G_CALLBACK (source_selected_cb), pitip);

            if (source != NULL) {
                itip_view_set_source (view, source);
                g_object_unref (source);

                /* FIXME Shouldn't the buttons be sensitized here? */
            } else {
                itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR, _("Unable to find any calendars"));
                itip_view_set_buttons_sensitive (view, FALSE);
            }
        } else if (!pitip->current_client) {
            switch (pitip->type) {
            case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                itip_view_add_lower_info_item_printf (
                    view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
                    _("Unable to find this meeting in any calendar"));
                break;
            case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
                itip_view_add_lower_info_item_printf (
                    view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
                    _("Unable to find this task in any task list"));
                break;
            case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
                itip_view_add_lower_info_item_printf (
                    view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
                    _("Unable to find this memo in any memo list"));
                break;
            default:
                g_assert_not_reached ();
                break;
            }
        }
    }

    if (fd->count == 0) {
        g_hash_table_destroy (fd->conflicts);
        g_cancellable_disconnect (fd->itip_cancellable, fd->cancelled_id);
        g_object_unref (fd->cancellable);
        g_object_unref (fd->itip_cancellable);
        g_object_unref (fd->view);
        g_free (fd->uid);
        g_free (fd->rid);
        if (fd->sexp)
            g_free (fd->sexp);
        g_free (fd);
    }
}

static void
get_object_without_rid_ready_cb (GObject *source_object,
                                 GAsyncResult *result,
                                 gpointer user_data)
{
    ECalClient *cal_client = E_CAL_CLIENT (source_object);
    FormatItipFindData *fd = user_data;
    icalcomponent *icalcomp = NULL;
    GError *error = NULL;

    if (!e_cal_client_get_object_finish (cal_client, result, &icalcomp, &error))
        icalcomp = NULL;

    if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
        g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
        g_cancellable_is_cancelled (fd->cancellable)) {
        g_clear_error (&error);
        find_cal_update_ui (fd, cal_client);
        decrease_find_data (fd);
        return;
    }

    g_clear_error (&error);

    if (icalcomp) {
        ECalComponent *comp;

        fd->puri->current_client = cal_client;
        fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method ==  ICAL_METHOD_REQUEST) &&
            (icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
            icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
            icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
            icalcomponent_get_first_component (icalcomp, ICAL_XPROCEDUREALARM_COMPONENT) ||
            icalcomponent_get_first_component (icalcomp, ICAL_XEMAILALARM_COMPONENT));

        comp = e_cal_component_new_from_icalcomponent (icalcomp);
        if (comp) {
            ESource *source = e_client_get_source (E_CLIENT (cal_client));

            g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp);
        }

        find_cal_update_ui (fd, cal_client);
        decrease_find_data (fd);
        return;
    }

    find_cal_update_ui (fd, cal_client);
    decrease_find_data (fd);
}

static void
get_object_with_rid_ready_cb (GObject *source_object,
                              GAsyncResult *result,
                              gpointer user_data)
{
    ECalClient *cal_client = E_CAL_CLIENT (source_object);
    FormatItipFindData *fd = user_data;
    icalcomponent *icalcomp = NULL;
    GError *error = NULL;

    if (!e_cal_client_get_object_finish (cal_client, result, &icalcomp, &error))
        icalcomp = NULL;

    if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
        g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
        g_cancellable_is_cancelled (fd->cancellable)) {
        g_clear_error (&error);
        find_cal_update_ui (fd, cal_client);
        decrease_find_data (fd);
        return;
    }

    g_clear_error (&error);

    if (icalcomp) {
        ECalComponent *comp;

        fd->puri->current_client = cal_client;
        fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method ==  ICAL_METHOD_REQUEST) &&
            (icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
            icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
            icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
            icalcomponent_get_first_component (icalcomp, ICAL_XPROCEDUREALARM_COMPONENT) ||
            icalcomponent_get_first_component (icalcomp, ICAL_XEMAILALARM_COMPONENT));

        comp = e_cal_component_new_from_icalcomponent (icalcomp);
        if (comp) {
            ESource *source = e_client_get_source (E_CLIENT (cal_client));

            g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp);
        }

        find_cal_update_ui (fd, cal_client);
        decrease_find_data (fd);
        return;
    }

    if (fd->rid && *fd->rid) {
        e_cal_client_get_object (cal_client, fd->uid, NULL, fd->cancellable, get_object_without_rid_ready_cb, fd);
        return;
    }

    find_cal_update_ui (fd, cal_client);
    decrease_find_data (fd);
}

static void
get_object_list_ready_cb (GObject *source_object,
                          GAsyncResult *result,
                          gpointer user_data)
{
    ECalClient *cal_client = E_CAL_CLIENT (source_object);
    FormatItipFindData *fd = user_data;
    GSList *objects = NULL;
    GError *error = NULL;

    if (!e_cal_client_get_object_list_finish (cal_client, result, &objects, &error))
        objects = NULL;

    if (g_cancellable_is_cancelled (fd->cancellable)) {
        g_clear_error (&error);
        decrease_find_data (fd);
        return;
    }

    if (error) {
        if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
            g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
            g_error_free (error);
            decrease_find_data (fd);
            return;
        }

        g_error_free (error);
    } else {
        g_hash_table_insert (fd->conflicts, cal_client, GINT_TO_POINTER (g_slist_length (objects)));
        e_cal_client_free_icalcomp_slist (objects);
    }

    e_cal_client_get_object (cal_client, fd->uid, fd->rid, fd->cancellable, get_object_with_rid_ready_cb, fd);
}

static void
find_cal_opened_cb (GObject *source_object,
                    GAsyncResult *result,
                    gpointer user_data)
{
    FormatItipFindData *fd = user_data;
    EMailPartItip *pitip = fd->puri;
    ItipView *view = fd->view;
    EClient *client;
    ESource *source;
    ECalClient *cal_client;
    gboolean search_for_conflicts = FALSE;
    const gchar *extension_name;
    GError *error = NULL;

    client = e_client_cache_get_client_finish (
        E_CLIENT_CACHE (source_object), result, &error);

    /* Sanity check. */
    g_return_if_fail (
        ((client != NULL) && (error == NULL)) ||
        ((client == NULL) && (error != NULL)));

    /* Ignore cancellations. */
    if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
        g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
        decrease_find_data (fd);
        g_error_free (error);
        return;
    }

    if (g_cancellable_is_cancelled (fd->cancellable)) {
        g_clear_error (&error);
        decrease_find_data (fd);
        return;
    }

    if (error) {
        /* FIXME Do we really want to warn here?  If we fail
         * to find the item, this won't be cleared but the
         * selector might be shown */
        add_failed_to_load_msg (view, error);
        decrease_find_data (fd);
        g_error_free (error);
        return;
    }

    /* Do not process read-only calendars */
    if (e_client_is_readonly (client)) {
        g_object_unref (client);
        decrease_find_data (fd);
        return;
    }

    cal_client = E_CAL_CLIENT (client);

    source = e_client_get_source (client);

    extension_name = E_SOURCE_EXTENSION_CONFLICT_SEARCH;
    if (e_source_has_extension (source, extension_name)) {
        ESourceConflictSearch *extension;

        extension = e_source_get_extension (source, extension_name);
        search_for_conflicts =
            (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) &&
            e_source_conflict_search_get_include_me (extension);
    }

    /* Check for conflicts */
    /* If the query fails, we'll just ignore it */
    /* FIXME What happens for recurring conflicts? */
    if (search_for_conflicts) {
        e_cal_client_get_object_list (
            cal_client, fd->sexp,
            fd->cancellable,
            get_object_list_ready_cb, fd);
        return;
    }

    if (!pitip->current_client) {
        e_cal_client_get_object (
            cal_client, fd->uid, fd->rid,
            fd->cancellable,
            get_object_with_rid_ready_cb, fd);
        return;
    }

    decrease_find_data (fd);
}

static void
itip_cancellable_cancelled (GCancellable *itip_cancellable,
                            GCancellable *fd_cancellable)
{
    g_cancellable_cancel (fd_cancellable);
}

static void
find_server (EMailPartItip *pitip,
             ItipView *view,
             ECalComponent *comp)
{
    FormatItipFindData *fd = NULL;
    const gchar *uid;
    gchar *rid = NULL;
    CamelStore *parent_store;
    ESource *current_source = NULL;
    GList *list, *link;
    GList *conflict_list = NULL;
    const gchar *extension_name;
    const gchar *store_uid;

    g_return_if_fail (pitip->folder != NULL);

    switch (pitip->type) {
        case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
            extension_name = E_SOURCE_EXTENSION_CALENDAR;
            break;
        case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
            extension_name = E_SOURCE_EXTENSION_TASK_LIST;
            break;
        case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
            extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
            break;
        default:
            g_return_if_reached ();
    }

    list = e_source_registry_list_sources (
        view->priv->registry, extension_name);

    e_cal_component_get_uid (comp, &uid);
    rid = e_cal_component_get_recurid_as_string (comp);

    /* XXX Not sure what this was trying to do,
     *     but it propbably doesn't work anymore.
     *     Some comments would have been helpful. */
    parent_store = camel_folder_get_parent_store (pitip->folder);

    store_uid = camel_service_get_uid (CAMEL_SERVICE (parent_store));

    itip_view_set_buttons_sensitive (view, FALSE);

    for (link = list; link != NULL; link = g_list_next (link)) {
        ESource *source = E_SOURCE (link->data);
        gboolean search_for_conflicts = FALSE;
        const gchar *source_uid;

        extension_name = E_SOURCE_EXTENSION_CONFLICT_SEARCH;
        if (e_source_has_extension (source, extension_name)) {
            ESourceConflictSearch *extension;

            extension =
                e_source_get_extension (source, extension_name);
            search_for_conflicts =
                e_source_conflict_search_get_include_me (extension);
        }

        if (search_for_conflicts)
            conflict_list = g_list_prepend (
                conflict_list, g_object_ref (source));

        if (current_source != NULL)
            continue;

        source_uid = e_source_get_uid (source);
        if (g_strcmp0 (source_uid, store_uid) == 0) {
            current_source = source;
            conflict_list = g_list_prepend (
                conflict_list, g_object_ref (source));

            continue;
        }
    }

    if (current_source) {
        link = conflict_list;

        pitip->progress_info_id = itip_view_add_lower_info_item (
            view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
            _("Opening the calendar. Please wait..."));
    } else {
        link = list;
        pitip->progress_info_id = itip_view_add_lower_info_item (
            view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
            _("Searching for an existing version of this appointment"));
    }

    for (; link != NULL; link = g_list_next (link)) {
        ESource *source = E_SOURCE (link->data);

        if (!fd) {
            gchar *start = NULL, *end = NULL;

            fd = g_new0 (FormatItipFindData, 1);
            fd->puri = pitip;
            fd->view = g_object_ref (view);
            fd->itip_cancellable = g_object_ref (pitip->cancellable);
            fd->cancellable = g_cancellable_new ();
            fd->cancelled_id = g_cancellable_connect (
                fd->itip_cancellable,
                G_CALLBACK (itip_cancellable_cancelled), fd->cancellable, NULL);
            fd->conflicts = g_hash_table_new (g_direct_hash, g_direct_equal);
            fd->uid = g_strdup (uid);
            fd->rid = rid;
            /* avoid free this at the end */
            rid = NULL;

            if (pitip->start_time && pitip->end_time) {
                start = isodate_from_time_t (pitip->start_time);
                end = isodate_from_time_t (pitip->end_time);

                fd->sexp = g_strdup_printf (
                    "(and (occur-in-time-range? "
                    "(make-time \"%s\") "
                    "(make-time \"%s\")) "
                    "(not (uid? \"%s\")))",
                    start, end,
                    icalcomponent_get_uid (pitip->ical_comp));
            }

            g_free (start);
            g_free (end);
        }
        fd->count++;
        d (printf ("Increasing itip formatter search count to %d\n", fd->count));

        if (current_source == source)
            start_calendar_server (
                pitip, view, source, pitip->type,
                find_cal_opened_cb, fd);
        else
            start_calendar_server (
                pitip, view, source, pitip->type,
                find_cal_opened_cb, fd);
    }

    g_list_free_full (conflict_list, (GDestroyNotify) g_object_unref);
    g_list_free_full (list, (GDestroyNotify) g_object_unref);

    g_free (rid);
}

static gboolean
change_status (ESourceRegistry *registry,
               icalcomponent *ical_comp,
               const gchar *address,
               icalparameter_partstat status)
{
    icalproperty *prop;

    prop = find_attendee (ical_comp, address);
    if (prop) {
        icalparameter *param;

        icalproperty_remove_parameter (prop, ICAL_PARTSTAT_PARAMETER);
        param = icalparameter_new_partstat (status);
        icalproperty_add_parameter (prop, param);
    } else {
        icalparameter *param;

        if (address != NULL) {
            prop = icalproperty_new_attendee (address);
            icalcomponent_add_property (ical_comp, prop);

            param = icalparameter_new_role (ICAL_ROLE_OPTPARTICIPANT);
            icalproperty_add_parameter (prop, param);

            param = icalparameter_new_partstat (status);
            icalproperty_add_parameter (prop, param);
        } else {
            gchar *default_name = NULL;
            gchar *default_address = NULL;

            itip_get_default_name_and_address (
                registry, &default_name, &default_address);

            prop = icalproperty_new_attendee (default_address);
            icalcomponent_add_property (ical_comp, prop);

            param = icalparameter_new_cn (default_name);
            icalproperty_add_parameter (prop, param);

            param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT);
            icalproperty_add_parameter (prop, param);

            param = icalparameter_new_partstat (status);
            icalproperty_add_parameter (prop, param);

            g_free (default_name);
            g_free (default_address);
        }
    }

    return TRUE;
}

static void
message_foreach_part (CamelMimePart *part,
                      GSList **part_list)
{
    CamelDataWrapper *containee;
    gint parts, i;
    gint go = TRUE;

    if (!part)
        return;

    *part_list = g_slist_append (*part_list, part);

    containee = camel_medium_get_content (CAMEL_MEDIUM (part));

    if (containee == NULL)
        return;

    /* using the object types is more accurate than using the mime/types */
    if (CAMEL_IS_MULTIPART (containee)) {
        parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
        for (i = 0; go && i < parts; i++) {
            /* Reuse already declared *parts? */
            CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (containee), i);

            message_foreach_part (part, part_list);
        }
    } else if (CAMEL_IS_MIME_MESSAGE (containee)) {
        message_foreach_part ((CamelMimePart *) containee, part_list);
    }
}

static void
attachment_load_finished (EAttachment *attachment,
                          GAsyncResult *result,
                          gpointer user_data)
{
    struct {
        GFile *file;
        gboolean done;
    } *status = user_data;

    /* Should be no need to check for error here. */
    e_attachment_load_finish (attachment, result, NULL);

    status->done = TRUE;
}

static void
attachment_save_finished (EAttachment *attachment,
                          GAsyncResult *result,
                          gpointer user_data)
{
    GError *error = NULL;

    struct {
        GFile *file;
        gboolean done;
    } *status = user_data;

    status->file = e_attachment_save_finish (attachment, result, &error);
    status->done = TRUE;

    /* XXX Error handling needs improvement. */
    if (error != NULL) {
        g_warning ("%s", error->message);
        g_error_free (error);
    }
}

static gchar *
get_uri_for_part (CamelMimePart *mime_part)
{
    EAttachment *attachment;
    GFile *temp_directory;
    gchar *template;
    gchar *path;

    struct {
        GFile *file;
        gboolean done;
    } status;

    /* XXX Error handling leaves much to be desired. */

    template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
    path = e_mkdtemp (template);
    g_free (template);

    if (path == NULL)
        return NULL;

    temp_directory = g_file_new_for_path (path);
    g_free (path);

    attachment = e_attachment_new ();
    e_attachment_set_mime_part (attachment, mime_part);

    status.done = FALSE;

    e_attachment_load_async (
        attachment, (GAsyncReadyCallback)
        attachment_load_finished, &status);

    /* Loading should be instantaneous since we already have
     * the full content, but we still have to crank the main
     * loop until the callback gets triggered. */
    while (!status.done)
        gtk_main_iteration ();

    status.file = NULL;
    status.done = FALSE;

    e_attachment_save_async (
        attachment, temp_directory, (GAsyncReadyCallback)
        attachment_save_finished, &status);

    /* We can't return until we have results, so crank
     * the main loop until the callback gets triggered. */
    while (!status.done)
        gtk_main_iteration ();

    if (status.file != NULL) {
        path = g_file_get_path (status.file);
        g_object_unref (status.file);
    } else
        path = NULL;

    g_object_unref (attachment);
    g_object_unref (temp_directory);

    return path;
}

static void
update_item_progress_info (EMailPartItip *pitip,
                           ItipView *view,
                           const gchar *message)
{
    if (pitip->update_item_progress_info_id) {
        itip_view_remove_lower_info_item (view, pitip->update_item_progress_info_id);
        pitip->update_item_progress_info_id = 0;

        if (!message)
            itip_view_set_buttons_sensitive (view, TRUE);
    }

    if (pitip->update_item_error_info_id) {
        itip_view_remove_lower_info_item (view, pitip->update_item_error_info_id);
        pitip->update_item_error_info_id = 0;
    }

    if (message) {
        itip_view_set_buttons_sensitive (view, FALSE);
        pitip->update_item_progress_info_id =
            itip_view_add_lower_info_item (
                view,
                ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
                message);
    }
}

static void
finish_message_delete_with_rsvp (EMailPartItip *pitip,
                                 ItipView *view,
                                 ECalClient *client)
{
    if (pitip->delete_message && pitip->folder)
        camel_folder_delete_message (pitip->folder, pitip->uid);

    if (itip_view_get_rsvp (view)) {
        ECalComponent *comp = NULL;
        icalcomponent *ical_comp;
        icalproperty *prop;
        icalvalue *value;
        const gchar *attendee;
        gchar *comment;
        GSList *l, *list = NULL;
        gboolean found;

        comp = e_cal_component_clone (pitip->comp);
        if (comp == NULL)
            return;

        if (pitip->to_address == NULL)
            find_to_address (view, pitip, pitip->ical_comp, NULL);
        g_assert (pitip->to_address != NULL);

        ical_comp = e_cal_component_get_icalcomponent (comp);

        /* Remove all attendees except the one we are responding as */
        found = FALSE;
        for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
             prop != NULL;
             prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
            gchar *text;

            value = icalproperty_get_value (prop);
            if (!value)
                continue;

            attendee = icalvalue_get_string (value);

            text = g_strdup (itip_strip_mailto (attendee));
            text = g_strstrip (text);

            /* We do this to ensure there is at most one
             * attendee in the response */
            if (found || g_ascii_strcasecmp (pitip->to_address, text))
                list = g_slist_prepend (list, prop);
            else if (!g_ascii_strcasecmp (pitip->to_address, text))
                found = TRUE;
            g_free (text);
        }

        for (l = list; l; l = l->next) {
            prop = l->data;
            icalcomponent_remove_property (ical_comp, prop);
            icalproperty_free (prop);
        }
        g_slist_free (list);

        /* Add a comment if there user set one */
        comment = itip_view_get_rsvp_comment (view);
        if (comment) {
            GSList comments;
            ECalComponentText text;

            text.value = comment;
            text.altrep = NULL;

            comments.data = &text;
            comments.next = NULL;

            e_cal_component_set_comment_list (comp, &comments);

            g_free (comment);
        }

        e_cal_component_rescan (comp);

        if (itip_send_comp (
                view->priv->registry,
                E_CAL_COMPONENT_METHOD_REPLY,
                comp, pitip->current_client,
                pitip->top_level, NULL, NULL, TRUE, FALSE) &&
                pitip->folder) {
            camel_folder_set_message_flags (
                pitip->folder, pitip->uid,
                CAMEL_MESSAGE_ANSWERED,
                CAMEL_MESSAGE_ANSWERED);
        }

        g_object_unref (comp);
    }

    update_item_progress_info (pitip, view, NULL);
}

static void
receive_objects_ready_cb (GObject *ecalclient,
                          GAsyncResult *result,
                          gpointer user_data)
{
    ECalClient *client = E_CAL_CLIENT (ecalclient);
    ESource *source = e_client_get_source (E_CLIENT (client));
    ItipView *view = user_data;
    EMailPartItip *pitip = itip_view_get_mail_part (view);
    GError *error = NULL;

    e_cal_client_receive_objects_finish (client, result, &error);

    if (error != NULL) {
        if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
            !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
            update_item_progress_info (pitip, view, NULL);
            pitip->update_item_error_info_id =
                itip_view_add_lower_info_item_printf (
                    view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
                    _("Unable to send item to calendar '%s'.  %s"),
                    e_source_get_display_name (source),
                    error->message);
        }
        g_error_free (error);
        return;
    }

    itip_view_set_extension_name (view, NULL);

    itip_view_clear_lower_info_items (view);

    switch (pitip->update_item_response) {
    case ITIP_VIEW_RESPONSE_ACCEPT:
        itip_view_add_lower_info_item_printf (
            view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
            _("Sent to calendar '%s' as accepted"), e_source_get_display_name (source));
        break;
    case ITIP_VIEW_RESPONSE_TENTATIVE:
        itip_view_add_lower_info_item_printf (
            view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
            _("Sent to calendar '%s' as tentative"), e_source_get_display_name (source));
        break;
    case ITIP_VIEW_RESPONSE_DECLINE:
        /* FIXME some calendars just might not save it at all, is this accurate? */
        itip_view_add_lower_info_item_printf (
            view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
            _("Sent to calendar '%s' as declined"), e_source_get_display_name (source));
        break;
    case ITIP_VIEW_RESPONSE_CANCEL:
        /* FIXME some calendars just might not save it at all, is this accurate? */
        itip_view_add_lower_info_item_printf (
            view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
            _("Sent to calendar '%s' as canceled"), e_source_get_display_name (source));
        break;
    default:
        g_assert_not_reached ();
        break;
    }

    finish_message_delete_with_rsvp (pitip, view, client);
}

static void
update_item (EMailPartItip *pitip,
             ItipView *view,
             ItipViewResponse response)
{
    struct icaltimetype stamp;
    icalproperty *prop;
    icalcomponent *clone;
    ECalComponent *clone_comp;
    gchar *str;

    update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));

    /* Set X-MICROSOFT-CDO-REPLYTIME to record the time at which
     * the user accepted/declined the request. (Outlook ignores
     * SEQUENCE in REPLY reponses and instead requires that each
     * updated response have a later REPLYTIME than the previous
     * one.) This also ends up getting saved in our own copy of
     * the meeting, though there's currently no way to see that
     * information (unless it's being saved to an Exchange folder
     * and you then look at it in Outlook).
     */
    stamp = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
    str = icaltime_as_ical_string_r (stamp);
    prop = icalproperty_new_x (str);
    g_free (str);
    icalproperty_set_x_name (prop, "X-MICROSOFT-CDO-REPLYTIME");
    icalcomponent_add_property (pitip->ical_comp, prop);

    clone = icalcomponent_new_clone (pitip->ical_comp);
    icalcomponent_add_component (pitip->top_level, clone);
    icalcomponent_set_method (pitip->top_level, pitip->method);

    if (!itip_view_get_inherit_alarm_check_state (view)) {
        icalcomponent *alarm_comp;
        icalcompiter alarm_iter;

        alarm_iter = icalcomponent_begin_component (clone, ICAL_VALARM_COMPONENT);
        while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) {
            icalcompiter_next (&alarm_iter);

            icalcomponent_remove_component (clone, alarm_comp);
            icalcomponent_free (alarm_comp);
        }
    }

    clone_comp = e_cal_component_new ();
    if (!e_cal_component_set_icalcomponent (clone_comp, clone)) {
        update_item_progress_info (pitip, view, NULL);
        pitip->update_item_error_info_id =
            itip_view_add_lower_info_item (
                view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
                _("Unable to parse item"));
        goto cleanup;
    }

    if (itip_view_get_keep_alarm_check_state (view)) {
        ECalComponent *real_comp;
        GList *alarms, *l;
        ECalComponentAlarm *alarm;

        real_comp = get_real_item (pitip);
        if (real_comp != NULL) {
            alarms = e_cal_component_get_alarm_uids (real_comp);

            for (l = alarms; l; l = l->next) {
                alarm = e_cal_component_get_alarm (
                    real_comp, (const gchar *) l->data);

                if (alarm) {
                    ECalComponentAlarm *aclone = e_cal_component_alarm_clone (alarm);

                    if (aclone) {
                        e_cal_component_add_alarm (clone_comp, aclone);
                        e_cal_component_alarm_free (aclone);
                    }

                    e_cal_component_alarm_free (alarm);
                }
            }

            cal_obj_uid_list_free (alarms);
            g_object_unref (real_comp);
        }
    }

    if ((response != ITIP_VIEW_RESPONSE_CANCEL)
        && (response != ITIP_VIEW_RESPONSE_DECLINE)) {
        GSList *attachments = NULL, *new_attachments = NULL, *l;
        CamelMimeMessage *msg = pitip->msg;

        e_cal_component_get_attachment_list (clone_comp, &attachments);

        for (l = attachments; l; l = l->next) {
            GSList *parts = NULL, *m;
            gchar *uri, *new_uri;
            CamelMimePart *part;

            uri = l->data;

            if (!g_ascii_strncasecmp (uri, "cid:...", 7)) {
                message_foreach_part ((CamelMimePart *) msg, &parts);

                for (m = parts; m; m = m->next) {
                    part = m->data;

                    /* Skip the actual message and the text/calendar part */
                    /* FIXME Do we need to skip anything else? */
                    if (part == (CamelMimePart *) msg || part == pitip->part)
                        continue;

                    new_uri = get_uri_for_part (part);
                    if (new_uri != NULL)
                        new_attachments = g_slist_append (new_attachments, new_uri);
                }

                g_slist_free (parts);

            } else if (!g_ascii_strncasecmp (uri, "cid:", 4)) {
                part = camel_mime_message_get_part_by_content_id (msg, uri + 4);
                if (part) {
                    new_uri = get_uri_for_part (part);
                    if (new_uri != NULL)
                        new_attachments = g_slist_append (new_attachments, new_uri);
                }

            } else {
                /* Preserve existing non-cid ones */
                new_attachments = g_slist_append (new_attachments, g_strdup (uri));
            }
        }

        g_slist_foreach (attachments, (GFunc) g_free, NULL);
        g_slist_free (attachments);

        e_cal_component_set_attachment_list (clone_comp, new_attachments);
    }

    pitip->update_item_response = response;

    e_cal_client_receive_objects (
        pitip->current_client,
        pitip->top_level,
        pitip->cancellable,
        receive_objects_ready_cb,
        view);

 cleanup:
    icalcomponent_remove_component (pitip->top_level, clone);
    g_object_unref (clone_comp);
}

/* TODO These operations should be available in e-cal-component.c */
static void
set_attendee (ECalComponent *comp,
              const gchar *address)
{
    icalproperty *prop;
    icalcomponent *icalcomp;
    gboolean found = FALSE;

    icalcomp = e_cal_component_get_icalcomponent (comp);

    for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
            prop;
            prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
        const gchar *attendee = icalproperty_get_attendee (prop);

        if (!(g_str_equal (itip_strip_mailto (attendee), address)))
            icalcomponent_remove_property (icalcomp, prop);
        else
            found = TRUE;
    }

    if (!found) {
        icalparameter *param;
        gchar *temp = g_strdup_printf ("MAILTO:%s", address);

        prop = icalproperty_new_attendee ((const gchar *) temp);
        icalcomponent_add_property (icalcomp, prop);

        param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION);
        icalproperty_add_parameter (prop, param);

        param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT);
        icalproperty_add_parameter (prop, param);

        param = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL);
        icalproperty_add_parameter (prop, param);

        param = icalparameter_new_rsvp (ICAL_RSVP_TRUE);
        icalproperty_add_parameter (prop, param);

        g_free (temp);
    }

}

static gboolean
send_comp_to_attendee (ESourceRegistry *registry,
                       ECalComponentItipMethod method,
                       ECalComponent *comp,
                       const gchar *user,
                       ECalClient *client,
                       const gchar *comment)
{
    gboolean status;
    ECalComponent *send_comp = e_cal_component_clone (comp);

    set_attendee (send_comp, user);

    if (comment) {
        GSList comments;
        ECalComponentText text;

        text.value = comment;
        text.altrep = NULL;

        comments.data = &text;
        comments.next = NULL;

        e_cal_component_set_comment_list (send_comp, &comments);
    }

    /* FIXME send the attachments in the request */
    status = itip_send_comp (
        registry, method, send_comp,
        client, NULL, NULL, NULL, TRUE, FALSE);

    g_object_unref (send_comp);

    return status;
}

static void
remove_delegate (EMailPartItip *pitip,
                 ItipView *view,
                 const gchar *delegate,
                 const gchar *delegator,
                 ECalComponent *comp)
{
    gboolean status;
    gchar *comment;

    comment = g_strdup_printf (
        _("Organizer has removed the delegate %s "),
        itip_strip_mailto (delegate));

    /* send cancellation notice to delegate */
    status = send_comp_to_attendee (
        view->priv->registry,
        E_CAL_COMPONENT_METHOD_CANCEL, pitip->comp,
        delegate, pitip->current_client, comment);
    if (status != 0) {
        send_comp_to_attendee (
            view->priv->registry,
            E_CAL_COMPONENT_METHOD_REQUEST, pitip->comp,
            delegator, pitip->current_client, comment);
    }
    if (status != 0) {
        itip_view_add_lower_info_item (
            view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
            _("Sent a cancelation notice to the delegate"));
    } else {
        itip_view_add_lower_info_item (
            view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
            _("Could not send the cancelation notice to the delegate"));
    }

    g_free (comment);

}

static void
update_x (ECalComponent *pitip_comp,
          ECalComponent *comp)
{
    icalcomponent *itip_icalcomp = e_cal_component_get_icalcomponent (pitip_comp);
    icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);

    icalproperty *prop = icalcomponent_get_first_property (itip_icalcomp, ICAL_X_PROPERTY);
    while (prop) {
        const gchar *name = icalproperty_get_x_name (prop);
        if (!g_ascii_strcasecmp (name, "X-EVOLUTION-IS-REPLY")) {
            icalproperty *new_prop = icalproperty_new_x (icalproperty_get_x (prop));
            icalproperty_set_x_name (new_prop, "X-EVOLUTION-IS-REPLY");
            icalcomponent_add_property (icalcomp, new_prop);
        }
        prop = icalcomponent_get_next_property (itip_icalcomp, ICAL_X_PROPERTY);
    }

    e_cal_component_set_icalcomponent (comp, icalcomp);
}

static void
modify_object_cb (GObject *ecalclient,
                  GAsyncResult *result,
                  gpointer user_data)
{
    ECalClient *client = E_CAL_CLIENT (ecalclient);
    ItipView *view = user_data;
    EMailPartItip *pitip = itip_view_get_mail_part (view);
    GError *error = NULL;

    e_cal_client_modify_object_finish (client, result, &error);

    if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
        g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
        g_error_free (error);
        return;
    }

    if (error != NULL) {
        update_item_progress_info (pitip, view, NULL);
        pitip->update_item_error_info_id =
            itip_view_add_lower_info_item_printf (
                view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
                _("Unable to update attendee. %s"),
                error->message);
        g_error_free (error);
    } else {
        update_item_progress_info (pitip, view, NULL);
        itip_view_add_lower_info_item (
            view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
            _("Attendee status updated"));
    }
}

static void
update_attendee_status_icalcomp (EMailPartItip *pitip,
                                 ItipView *view,
                                 icalcomponent *icalcomp)
{
    ECalComponent *comp;
    const gchar *uid = NULL;
    gchar *rid;
    GSList *attendees;

    e_cal_component_get_uid (pitip->comp, &uid);
    rid = e_cal_component_get_recurid_as_string (pitip->comp);

    comp = e_cal_component_new ();
    if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
        icalcomponent_free (icalcomp);

        itip_view_add_lower_info_item (
            view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
            _("The meeting is invalid and cannot be updated"));
    } else {
        icalcomponent *org_icalcomp;
        const gchar *delegate;

        org_icalcomp = e_cal_component_get_icalcomponent (pitip->comp);

        e_cal_component_get_attendee_list (pitip->comp, &attendees);
        if (attendees != NULL) {
            ECalComponentAttendee *a = attendees->data;
            icalproperty *prop, *del_prop;
            EShell *shell = e_shell_get_default ();

            prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
            if ((a->status == ICAL_PARTSTAT_DELEGATED) && (del_prop = find_attendee (org_icalcomp, itip_strip_mailto (a->delto))) && !(find_attendee (icalcomp, itip_strip_mailto (a->delto)))) {
                gint response;
                delegate = icalproperty_get_attendee (del_prop);
                response = e_alert_run_dialog_for_args (
                    e_shell_get_active_window (shell),
                    "org.gnome.itip-formatter:add-delegate",
                    itip_strip_mailto (a->value),
                    itip_strip_mailto (delegate), NULL);
                if (response == GTK_RESPONSE_YES) {
                    icalcomponent_add_property (icalcomp, icalproperty_new_clone (del_prop));
                    e_cal_component_rescan (comp);
                } else if (response == GTK_RESPONSE_NO) {
                    remove_delegate (pitip, view, delegate, itip_strip_mailto (a->value), comp);
                    goto cleanup;
                } else {
                    goto cleanup;
                }
            }

            if (prop == NULL) {
                gint response;

                if (a->delfrom && *a->delfrom) {
                    response = e_alert_run_dialog_for_args (
                        e_shell_get_active_window (shell),
                        "org.gnome.itip-formatter:add-delegate",
                        itip_strip_mailto (a->delfrom),
                        itip_strip_mailto (a->value), NULL);
                    if (response == GTK_RESPONSE_YES) {
                        /* Already declared in this function */
                        icalproperty *prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
                        icalcomponent_add_property (icalcomp,icalproperty_new_clone (prop));
                        e_cal_component_rescan (comp);
                    } else if (response == GTK_RESPONSE_NO) {
                        remove_delegate (
                            pitip,
                            view,
                            itip_strip_mailto (a->value),
                            itip_strip_mailto (a->delfrom),
                            comp);
                        goto cleanup;
                    } else {
                        goto cleanup;
                    }
                }

                response = e_alert_run_dialog_for_args (
                    e_shell_get_active_window (shell),
                    "org.gnome.itip-formatter:add-unknown-attendee", NULL);

                if (response == GTK_RESPONSE_YES) {
                    change_status (
                        view->priv->registry, icalcomp,
                        itip_strip_mailto (a->value),
                        a->status);
                    e_cal_component_rescan (comp);
                } else {
                    goto cleanup;
                }
            } else if (a->status == ICAL_PARTSTAT_NONE || a->status == ICAL_PARTSTAT_X) {
                itip_view_add_lower_info_item (
                    view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
                    _("Attendee status could not be updated because the status is invalid"));
                goto cleanup;
            } else {
                if (a->status == ICAL_PARTSTAT_DELEGATED) {
                    /* *prop already declared in this function */
                    icalproperty *prop, *new_prop;

                    prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
                    icalcomponent_remove_property (icalcomp, prop);

                    new_prop = find_attendee (org_icalcomp, itip_strip_mailto (a->value));
                    icalcomponent_add_property (icalcomp, icalproperty_new_clone (new_prop));
                } else {
                    change_status (
                        view->priv->registry,icalcomp,
                        itip_strip_mailto (a->value),
                        a->status);
                }

                e_cal_component_rescan (comp);
            }
        }
    }

    update_x (pitip->comp, comp);

    if (itip_view_get_update (view)) {
        e_cal_component_commit_sequence (comp);
        itip_send_comp (
            view->priv->registry,
            E_CAL_COMPONENT_METHOD_REQUEST,
            comp, pitip->current_client,
            NULL, NULL, NULL, TRUE, FALSE);
    }

    update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));

    e_cal_client_modify_object (
        pitip->current_client,
        icalcomp, rid ? CALOBJ_MOD_THIS : CALOBJ_MOD_ALL,
        pitip->cancellable,
        modify_object_cb,
        view);

 cleanup:
    g_object_unref (comp);
}

static void
update_attendee_status_get_object_without_rid_cb (GObject *ecalclient,
                                                  GAsyncResult *result,
                                                  gpointer user_data)
{
    ECalClient *client = E_CAL_CLIENT (ecalclient);
    ItipView *view = user_data;
    EMailPartItip *pitip = itip_view_get_mail_part (view);
    icalcomponent *icalcomp = NULL;
    GError *error = NULL;

    if (!e_cal_client_get_object_finish (client, result, &icalcomp, &error)) {
        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
            g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
            g_clear_error (&error);
            return;
        }

        g_clear_error (&error);

        update_item_progress_info (pitip, view, NULL);
        pitip->update_item_error_info_id = itip_view_add_lower_info_item (
            view,
            ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
            _("Attendee status can not be updated because the item no longer exists"));
        return;
    }

    update_attendee_status_icalcomp (pitip, view, icalcomp);
}

static void
update_attendee_status_get_object_with_rid_cb (GObject *ecalclient,
                                               GAsyncResult *result,
                                               gpointer user_data)
{
    ECalClient *client = E_CAL_CLIENT (ecalclient);
    ItipView *view = user_data;
    EMailPartItip *pitip = itip_view_get_mail_part (view);
    icalcomponent *icalcomp = NULL;
    GError *error = NULL;

    if (!e_cal_client_get_object_finish (client, result, &icalcomp, &error)) {
        const gchar *uid;
        gchar *rid;

        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
            g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
            g_clear_error (&error);
            return;
        }

        g_clear_error (&error);

        e_cal_component_get_uid (pitip->comp, &uid);
        rid = e_cal_component_get_recurid_as_string (pitip->comp);

        if (!rid || !*rid) {
            g_free (rid);

            update_item_progress_info (pitip, view, NULL);
            pitip->update_item_error_info_id = itip_view_add_lower_info_item (
                view,
                ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
                _("Attendee status can not be updated because the item no longer exists"));
            return;
        }

        e_cal_client_get_object (
            pitip->current_client,
            uid,
            NULL,
            pitip->cancellable,
            update_attendee_status_get_object_without_rid_cb,
            view);

        g_free (rid);
        return;
    }

    update_attendee_status_icalcomp (pitip, view, icalcomp);
}

static void
update_attendee_status (EMailPartItip *pitip,
                        ItipView *view)
{
    const gchar *uid = NULL;
    gchar *rid;

    /* Obtain our version */
    e_cal_component_get_uid (pitip->comp, &uid);
    rid = e_cal_component_get_recurid_as_string (pitip->comp);

    update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));

    /* search for a master object if the detached object doesn't exist in the calendar */
    e_cal_client_get_object (
        pitip->current_client,
        uid, rid,
        pitip->cancellable,
        update_attendee_status_get_object_with_rid_cb,
        view);

    g_free (rid);
}

static void
send_item (EMailPartItip *pitip,
           ItipView *view)
{
    ECalComponent *comp;

    comp = get_real_item (pitip);

    if (comp != NULL) {
        itip_send_comp (
            view->priv->registry,
            E_CAL_COMPONENT_METHOD_REQUEST,
            comp, pitip->current_client,
            NULL, NULL, NULL, TRUE, FALSE);
        g_object_unref (comp);

        switch (pitip->type) {
        case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
            itip_view_add_lower_info_item (
                view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
                _("Meeting information sent"));
            break;
        case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
            itip_view_add_lower_info_item (
                view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
                _("Task information sent"));
            break;
        case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
            itip_view_add_lower_info_item (
                view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
                _("Memo information sent"));
            break;
        default:
            g_assert_not_reached ();
            break;
        }
    } else {
        switch (pitip->type) {
        case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
            itip_view_add_lower_info_item (
                view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
                _("Unable to send meeting information, the meeting does not exist"));
            break;
        case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
            itip_view_add_lower_info_item (
                view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
                _("Unable to send task information, the task does not exist"));
            break;
        case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
            itip_view_add_lower_info_item (
                view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
                _("Unable to send memo information, the memo does not exist"));
            break;
        default:
            g_assert_not_reached ();
            break;
        }
    }
}

static icalcomponent *
get_next (icalcompiter *iter)
{
    icalcomponent *ret = NULL;
    icalcomponent_kind kind;

    do {
        icalcompiter_next (iter);
        ret = icalcompiter_deref (iter);
        if (ret == NULL)
            break;
        kind = icalcomponent_isa (ret);
    } while (ret != NULL
         && kind != ICAL_VEVENT_COMPONENT
         && kind != ICAL_VTODO_COMPONENT
         && kind != ICAL_VFREEBUSY_COMPONENT);

    return ret;
}

static void
attachment_load_finish (EAttachment *attachment,
                        GAsyncResult *result,
                        GFile *file)
{
    EShell *shell;
    GtkWindow *parent;

    /* XXX Theoretically, this should never fail. */
    e_attachment_load_finish (attachment, result, NULL);

    shell = e_shell_get_default ();
    parent = e_shell_get_active_window (shell);

    e_attachment_save_async (
        attachment, file, (GAsyncReadyCallback)
        e_attachment_save_handle_error, parent);

    g_object_unref (file);
}

static void
save_vcalendar_cb (EMailPartItip *pitip)
{
    EAttachment *attachment;
    EShell *shell;
    GFile *file;
    const gchar *suggestion;

    g_return_if_fail (pitip != NULL);
    g_return_if_fail (pitip->vcalendar != NULL);
    g_return_if_fail (pitip->part != NULL);

    suggestion = camel_mime_part_get_filename (pitip->part);
    if (suggestion == NULL) {
        /* Translators: This is a default filename for a calendar. */
        suggestion = _("calendar.ics");
    }

    shell = e_shell_get_default ();
    file = e_shell_run_save_dialog (
        shell, _("Save Calendar"), suggestion, "*.ics:text/calendar", NULL, NULL);
    if (file == NULL)
        return;

    attachment = e_attachment_new ();
    e_attachment_set_mime_part (attachment, pitip->part);

    e_attachment_load_async (
        attachment, (GAsyncReadyCallback)
        attachment_load_finish, file);
}

static void
set_itip_error (ItipView *view,
                const gchar *primary,
                const gchar *secondary,
                gboolean save_btn)
{
    gchar *error;

    error = g_strdup_printf (
        "<div class=\"error\">"
        "<p><b>%s</b></p>"
        "<p>%s</p>",
        primary, secondary);

    itip_view_set_error (view, error, save_btn);

    g_free (error);
}

static gboolean
extract_itip_data (EMailPartItip *pitip,
                   ItipView *view,
                   gboolean *have_alarms)
{
    GSettings *settings;
    icalproperty *prop;
    icalcomponent_kind kind = ICAL_NO_COMPONENT;
    icalcomponent *tz_comp;
    icalcompiter tz_iter;
    icalcomponent *alarm_comp;
    icalcompiter alarm_iter;
    ECalComponent *comp;
    gboolean use_default_reminder;

    if (!pitip->vcalendar) {
        set_itip_error (
            view,
            _("The calendar attached is not valid"),
            _("The message claims to contain a calendar, but the calendar is not a valid iCalendar."),
            FALSE);

        return FALSE;
    }

    pitip->top_level = e_cal_util_new_top_level ();

    pitip->main_comp = icalparser_parse_string (pitip->vcalendar);
    if (pitip->main_comp == NULL || !is_icalcomp_valid (pitip->main_comp)) {
        set_itip_error (
            view,
            _("The calendar attached is not valid"),
            _("The message claims to contain a calendar, but the calendar is not a valid iCalendar."),
            FALSE);

        if (pitip->main_comp) {
            icalcomponent_free (pitip->main_comp);
            pitip->main_comp = NULL;
        }

        return FALSE;
    }

    prop = icalcomponent_get_first_property (pitip->main_comp, ICAL_METHOD_PROPERTY);
    if (prop == NULL) {
        pitip->method = ICAL_METHOD_PUBLISH;
    } else {
        pitip->method = icalproperty_get_method (prop);
    }

    tz_iter = icalcomponent_begin_component (pitip->main_comp, ICAL_VTIMEZONE_COMPONENT);
    while ((tz_comp = icalcompiter_deref (&tz_iter)) != NULL) {
        icalcomponent *clone;

        clone = icalcomponent_new_clone (tz_comp);
        icalcomponent_add_component (pitip->top_level, clone);

        icalcompiter_next (&tz_iter);
    }

    pitip->iter = icalcomponent_begin_component (pitip->main_comp, ICAL_ANY_COMPONENT);
    pitip->ical_comp = icalcompiter_deref (&pitip->iter);
    if (pitip->ical_comp != NULL) {
        kind = icalcomponent_isa (pitip->ical_comp);
        if (kind != ICAL_VEVENT_COMPONENT
            && kind != ICAL_VTODO_COMPONENT
            && kind != ICAL_VFREEBUSY_COMPONENT
            && kind != ICAL_VJOURNAL_COMPONENT)
            pitip->ical_comp = get_next (&pitip->iter);
    }

    if (pitip->ical_comp == NULL) {
        set_itip_error (
            view,
            _("The item in the calendar is not valid"),
            _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
            FALSE);

        return FALSE;
    }

    switch (icalcomponent_isa (pitip->ical_comp)) {
    case ICAL_VEVENT_COMPONENT:
        pitip->type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
        pitip->has_organizer = icalcomponent_get_first_property (pitip->ical_comp, ICAL_ORGANIZER_PROPERTY) != NULL;
        if (icalcomponent_get_first_property (pitip->ical_comp, ICAL_ATTENDEE_PROPERTY) == NULL) {
            /* no attendees: assume that that this is not a meeting and organizer doesn't want a reply */
            pitip->no_reply_wanted = TRUE;
        } else {
            /*
             * if we have attendees, then find_to_address() will check for our RSVP
             * and set no_reply_wanted=TRUE if RSVP=FALSE for the current user
             */
        }
        break;
    case ICAL_VTODO_COMPONENT:
        pitip->type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
        break;
    case ICAL_VJOURNAL_COMPONENT:
        pitip->type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
        break;
    default:
        set_itip_error (
            view,
            _("The item in the calendar is not valid"),
            _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
            FALSE);

        return FALSE;
    }

    pitip->total = icalcomponent_count_components (pitip->main_comp, ICAL_VEVENT_COMPONENT);
    pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VTODO_COMPONENT);
    pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VFREEBUSY_COMPONENT);
    pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VJOURNAL_COMPONENT);

    if (pitip->total > 1) {

        set_itip_error (
            view,
            _("The calendar attached contains multiple items"),
            _("To process all of these items, the file should be saved and the calendar imported"),
            TRUE);

    } if (pitip->total > 0) {
        pitip->current = 1;
    } else {
        pitip->current = 0;
    }

    if (icalcomponent_isa (pitip->ical_comp) != ICAL_VJOURNAL_COMPONENT) {
        gchar *my_address;

        prop = NULL;
        comp = e_cal_component_new ();
        e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (pitip->ical_comp));
        my_address = itip_get_comp_attendee (
            view->priv->registry, comp, NULL);
        g_object_unref (comp);
        comp = NULL;

        if (!prop)
            prop = find_attendee (pitip->ical_comp, my_address);
        if (!prop)
            prop = find_attendee_if_sentby (pitip->ical_comp, my_address);
        if (prop) {
            icalparameter *param;
            const gchar * delfrom;

            if ((param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER))) {
                delfrom = icalparameter_get_delegatedfrom (param);

                pitip->delegator_address = g_strdup (itip_strip_mailto (delfrom));
            }
        }
        g_free (my_address);
        prop = NULL;

        /* Determine any delegate sections */
        prop = icalcomponent_get_first_property (pitip->ical_comp, ICAL_X_PROPERTY);
        while (prop) {
            const gchar *x_name, *x_val;

            x_name = icalproperty_get_x_name (prop);
            x_val = icalproperty_get_x (prop);

            if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-UID"))
                pitip->calendar_uid = g_strdup (x_val);
            else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-URI"))
                g_warning (G_STRLOC ": X-EVOLUTION-DELEGATOR-CALENDAR-URI used");
            else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-ADDRESS"))
                pitip->delegator_address = g_strdup (x_val);
            else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-NAME"))
                pitip->delegator_name = g_strdup (x_val);

            prop = icalcomponent_get_next_property (pitip->ical_comp, ICAL_X_PROPERTY);
        }

        /* Strip out procedural alarms for security purposes */
        alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT);
        while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) {
            icalproperty *p;

            icalcompiter_next (&alarm_iter);

            p = icalcomponent_get_first_property (alarm_comp, ICAL_ACTION_PROPERTY);
            if (!p || icalproperty_get_action (p) == ICAL_ACTION_PROCEDURE)
                icalcomponent_remove_component (pitip->ical_comp, alarm_comp);

            icalcomponent_free (alarm_comp);
        }

        if (have_alarms) {
            alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT);
            *have_alarms = icalcompiter_deref (&alarm_iter) != NULL;
        }
    }

    pitip->comp = e_cal_component_new ();
    if (!e_cal_component_set_icalcomponent (pitip->comp, pitip->ical_comp)) {
        g_object_unref (pitip->comp);
        pitip->comp = NULL;

        set_itip_error (
            view,
            _("The item in the calendar is not valid"),
            _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
            FALSE);

        return FALSE;
    };

    /* Add default reminder if the config says so */

    settings = g_settings_new ("org.gnome.evolution.calendar");

    use_default_reminder =
        g_settings_get_boolean (settings, "use-default-reminder");

    if (use_default_reminder) {
        ECalComponentAlarm *acomp;
        gint interval;
        EDurationType units;
        ECalComponentAlarmTrigger trigger;

        interval = g_settings_get_int (
            settings, "default-reminder-interval");
        units = g_settings_get_enum (
            settings, "default-reminder-units");

        acomp = e_cal_component_alarm_new ();

        e_cal_component_alarm_set_action (acomp, E_CAL_COMPONENT_ALARM_DISPLAY);

        trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
        memset (&trigger.u.rel_duration, 0, sizeof (trigger.u.rel_duration));

        trigger.u.rel_duration.is_neg = TRUE;

        switch (units) {
            case E_DURATION_MINUTES:
                trigger.u.rel_duration.minutes = interval;
                break;
            case E_DURATION_HOURS:
                trigger.u.rel_duration.hours = interval;
                break;
            case E_DURATION_DAYS:
                trigger.u.rel_duration.days = interval;
                break;
            default:
                g_assert_not_reached ();
        }

        e_cal_component_alarm_set_trigger (acomp, trigger);
        e_cal_component_add_alarm (pitip->comp, acomp);

        e_cal_component_alarm_free (acomp);
    }

    g_object_unref (settings);

    find_from_address (view, pitip, pitip->ical_comp);
    find_to_address (view, pitip, pitip->ical_comp, NULL);

    return TRUE;
}

static gboolean
idle_open_cb (gpointer data)
{
    EMailPartItip *pitip = data;
    EShell *shell;
    const gchar *uris[2];
    gchar *start, *end, *shell_uri;

    start = isodate_from_time_t (pitip->start_time ? pitip->start_time : time (NULL));
    end = isodate_from_time_t (pitip->end_time ? pitip->end_time : time (NULL));
    shell_uri = g_strdup_printf ("calendar:///?startdate=%s&enddate=%s", start, end);

    uris[0] = shell_uri;
    uris[1] = NULL;

    shell = e_shell_get_default ();
    e_shell_handle_uris (shell, uris, FALSE);

    g_free (shell_uri);
    g_free (start);
    g_free (end);

    return FALSE;
}

static void
view_response_cb (ItipView *view,
                  ItipViewResponse response,
                  gpointer data)
{
    EMailPartItip *pitip = data;
    gboolean status = FALSE;
    icalproperty *prop;
    ECalComponentTransparency trans;

    if (response == ITIP_VIEW_RESPONSE_SAVE) {
        save_vcalendar_cb (pitip);
        return;
    }

    if (pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST) {
        if (itip_view_get_free_time_check_state (view))
            e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
        else
            e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
    } else {
        e_cal_component_get_transparency (pitip->comp, &trans);

        if (trans == E_CAL_COMPONENT_TRANSP_NONE)
            e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
    }

    if (!pitip->to_address && pitip->current_client != NULL)
        e_client_get_backend_property_sync (E_CLIENT (pitip->current_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &pitip->to_address, NULL, NULL);

    /* check if it is a  recur instance (no master object) and
     * add a property */
    if (itip_view_get_recur_check_state (view)) {
        prop = icalproperty_new_x ("All");
        icalproperty_set_x_name (prop, "X-GW-RECUR-INSTANCES-MOD-TYPE");
        icalcomponent_add_property (pitip->ical_comp, prop);
    }

    switch (response) {
        case ITIP_VIEW_RESPONSE_ACCEPT:
            if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
                status = change_status (
                    view->priv->registry,
                    pitip->ical_comp,
                    pitip->to_address,
                    ICAL_PARTSTAT_ACCEPTED);
            else
                status = TRUE;
            if (status) {
                e_cal_component_rescan (pitip->comp);
                update_item (pitip, view, response);
            }
            break;
        case ITIP_VIEW_RESPONSE_TENTATIVE:
            status = change_status (
                    view->priv->registry,
                    pitip->ical_comp,
                    pitip->to_address,
                    ICAL_PARTSTAT_TENTATIVE);
            if (status) {
                e_cal_component_rescan (pitip->comp);
                update_item (pitip, view, response);
            }
            break;
        case ITIP_VIEW_RESPONSE_DECLINE:
            if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
                status = change_status (
                    view->priv->registry,
                    pitip->ical_comp,
                    pitip->to_address,
                    ICAL_PARTSTAT_DECLINED);
            else {
                prop = icalproperty_new_x ("1");
                icalproperty_set_x_name (prop, "X-GW-DECLINED");
                icalcomponent_add_property (pitip->ical_comp, prop);
                status = TRUE;
            }

            if (status) {
                e_cal_component_rescan (pitip->comp);
                update_item (pitip, view, response);
            }
            break;
        case ITIP_VIEW_RESPONSE_UPDATE:
            update_attendee_status (pitip, view);
            break;
        case ITIP_VIEW_RESPONSE_CANCEL:
            update_item (pitip, view, response);
            break;
        case ITIP_VIEW_RESPONSE_REFRESH:
            send_item (pitip, view);
            break;
        case ITIP_VIEW_RESPONSE_OPEN:
            /* Prioritize ahead of GTK+ redraws. */
            g_idle_add_full (
                G_PRIORITY_HIGH_IDLE,
                idle_open_cb, pitip, NULL);
            return;
        default:
            break;
    }
}

static gboolean
check_is_instance (icalcomponent *icalcomp)
{
    icalproperty *icalprop;

    icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
    while (icalprop) {
        const gchar *x_name;

        x_name = icalproperty_get_x_name (icalprop);
        if (!strcmp (x_name, "X-GW-RECURRENCE-KEY")) {
            return TRUE;
        }
        icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
    }

    return FALSE;
}

static gboolean
in_proper_folder (CamelFolder *folder)
{
    EShell *shell;
    EShellBackend *shell_backend;
    EMailBackend *backend;
    EMailSession *session;
    MailFolderCache *folder_cache;
    ESourceRegistry *registry;
    CamelStore *store;
    const gchar *folder_name;
    gboolean res = TRUE;
    CamelFolderInfoFlags flags = 0;
    gboolean have_flags;

    if (folder == NULL)
        return FALSE;

    shell = e_shell_get_default ();
    registry = e_shell_get_registry (shell);
    shell_backend = e_shell_get_backend_by_name (shell, "mail");
    backend = E_MAIL_BACKEND (shell_backend);
    session = e_mail_backend_get_session (backend);
    folder_cache = e_mail_session_get_folder_cache (session);

    store = camel_folder_get_parent_store (folder);
    folder_name = camel_folder_get_full_name (folder);

    have_flags = mail_folder_cache_get_folder_info_flags (
        folder_cache, store, folder_name, &flags);

    if (have_flags) {
        /* it should be neither trash nor junk folder, */
        res = ((flags & CAMEL_FOLDER_TYPE_MASK) !=  CAMEL_FOLDER_TYPE_TRASH &&
               (flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_JUNK &&
              /* it can be Inbox */
            ((flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX ||
              /* or any other virtual folder */
              CAMEL_IS_VEE_FOLDER (folder) ||
              /* or anything else except of sent, outbox or drafts folder */
              (!em_utils_folder_is_sent (registry, folder) &&
               !em_utils_folder_is_outbox (registry, folder) &&
               !em_utils_folder_is_drafts (registry, folder))
            ));
    } else {
        /* cannot check for Inbox folder here */
        res = (folder->folder_flags & (CAMEL_FOLDER_IS_TRASH | CAMEL_FOLDER_IS_JUNK)) == 0 && (
              (CAMEL_IS_VEE_FOLDER (folder)) || (
              !em_utils_folder_is_sent (registry, folder) &&
              !em_utils_folder_is_outbox (registry, folder) &&
              !em_utils_folder_is_drafts (registry, folder)));
    }

    return res;
}

void
itip_view_init_view (ItipView *view)
{
    EShell *shell;
    EClientCache *client_cache;
    ECalComponentText text;
    ECalComponentOrganizer organizer;
    ECalComponentDateTime datetime;
    icaltimezone *from_zone;
    icaltimezone *to_zone = NULL;
    GSettings *settings;
    GString *gstring = NULL;
    GSList *list, *l;
    icalcomponent *icalcomp;
    const gchar *string, *org;
    gboolean response_enabled;
    gboolean have_alarms = FALSE;
    EMailPartItip *info;

    info = view->priv->itip_part;
    g_return_if_fail (info != NULL);

    shell = e_shell_get_default ();
    client_cache = e_shell_get_client_cache (shell);

    info->client_cache = g_object_ref (client_cache);

        /* Reset current client before initializing view */
    info->current_client = NULL;

        /* FIXME Handle multiple VEVENTS with the same UID, ie detached instances */
    if (!extract_itip_data (info, view, &have_alarms))
        return;

    response_enabled = in_proper_folder (info->folder);

    if (!response_enabled) {
        itip_view_set_mode (view, ITIP_VIEW_MODE_HIDE_ALL);
    } else {
        itip_view_set_show_inherit_alarm_check (
            view,
            have_alarms && (info->method == ICAL_METHOD_PUBLISH || info->method ==  ICAL_METHOD_REQUEST));

        switch (info->method) {
            case ICAL_METHOD_PUBLISH:
            case ICAL_METHOD_REQUEST:
                                /*
                                 * Treat meeting request (sent by organizer directly) and
                                 * published evend (forwarded by organizer or attendee) alike:
                                 * if the event has an organizer, then it can be replied to and
                                 * we show the "accept/tentative/decline" choice.
                                 * Otherwise only show "accept".
                                 */
                itip_view_set_mode (
                    view,
                    info->has_organizer ?
                    ITIP_VIEW_MODE_REQUEST :
                    ITIP_VIEW_MODE_PUBLISH);
                break;
            case ICAL_METHOD_REPLY:
                itip_view_set_mode (view, ITIP_VIEW_MODE_REPLY);
                break;
            case ICAL_METHOD_ADD:
                itip_view_set_mode (view, ITIP_VIEW_MODE_ADD);
                break;
            case ICAL_METHOD_CANCEL:
                itip_view_set_mode (view, ITIP_VIEW_MODE_CANCEL);
                break;
            case ICAL_METHOD_REFRESH:
                itip_view_set_mode (view, ITIP_VIEW_MODE_REFRESH);
                break;
            case ICAL_METHOD_COUNTER:
                itip_view_set_mode (view, ITIP_VIEW_MODE_COUNTER);
                break;
            case ICAL_METHOD_DECLINECOUNTER:
                itip_view_set_mode (view, ITIP_VIEW_MODE_DECLINECOUNTER);
                break;
            case ICAL_METHOD_X :
                                /* Handle appointment requests from Microsoft Live. This is
                                 * a best-at-hand-now handling. Must be revisited when we have
                                 * better access to the source of such meetings */
                info->method = ICAL_METHOD_REQUEST;
                itip_view_set_mode (view, ITIP_VIEW_MODE_REQUEST);
                break;
            default:
                return;
        }
    }

    itip_view_set_item_type (view, info->type);

    if (response_enabled) {
        switch (info->method) {
            case ICAL_METHOD_REQUEST:
                                /* FIXME What about the name? */
                itip_view_set_delegator (view, info->delegator_name ? info->delegator_name : info->delegator_address);
            case ICAL_METHOD_PUBLISH:
            case ICAL_METHOD_ADD:
            case ICAL_METHOD_CANCEL:
            case ICAL_METHOD_DECLINECOUNTER:
                itip_view_set_show_update_check (view, FALSE);

                                /* An organizer sent this */
                e_cal_component_get_organizer (info->comp, &organizer);
                org = organizer.cn ? organizer.cn : itip_strip_mailto (organizer.value);

                itip_view_set_organizer (view, org);
                if (organizer.sentby)
                    itip_view_set_organizer_sentby (
                        view, itip_strip_mailto (organizer.sentby));

                    if (info->my_address) {
                        if (!(organizer.value && !g_ascii_strcasecmp (itip_strip_mailto (organizer.value), info->my_address))
                            && !(organizer.sentby && !g_ascii_strcasecmp (itip_strip_mailto (organizer.sentby), info->my_address))
                            && (info->to_address && g_ascii_strcasecmp (info->to_address, info->my_address)))
                            itip_view_set_proxy (view, info->to_name ? info->to_name : info->to_address);
                    }
                    break;
            case ICAL_METHOD_REPLY:
            case ICAL_METHOD_REFRESH:
            case ICAL_METHOD_COUNTER:
                itip_view_set_show_update_check (view, TRUE);

                                /* An attendee sent this */
                e_cal_component_get_attendee_list (info->comp, &list);
                if (list != NULL) {
                    ECalComponentAttendee *attendee;

                    attendee = list->data;

                    itip_view_set_attendee (view, attendee->cn ? attendee->cn : itip_strip_mailto (attendee->value));

                    if (attendee->sentby)
                        itip_view_set_attendee_sentby (view, itip_strip_mailto (attendee->sentby));

                    if (info->my_address) {
                        if (!(attendee->value && !g_ascii_strcasecmp (itip_strip_mailto (attendee->value), info->my_address))
                            && !(attendee->sentby && !g_ascii_strcasecmp (itip_strip_mailto (attendee->sentby), info->my_address))
                            && (info->from_address && g_ascii_strcasecmp (info->from_address, info->my_address)))
                            itip_view_set_proxy (view, info->from_name ? info->from_name : info->from_address);
                    }

                    e_cal_component_free_attendee_list (list);
                }
                break;
            default:
                g_assert_not_reached ();
                break;
        }
    }

    e_cal_component_get_summary (info->comp, &text);
    itip_view_set_summary (view, text.value ? text.value : C_("cal-itip", "None"));

    e_cal_component_get_location (info->comp, &string);
    itip_view_set_location (view, string);

        /* Status really only applies for REPLY */
    if (response_enabled && info->method == ICAL_METHOD_REPLY) {
        e_cal_component_get_attendee_list (info->comp, &list);
        if (list != NULL) {
            ECalComponentAttendee *a = list->data;

            switch (a->status) {
                case ICAL_PARTSTAT_ACCEPTED:
                    itip_view_set_status (view, _("Accepted"));
                    break;
                case ICAL_PARTSTAT_TENTATIVE:
                    itip_view_set_status (view, _("Tentatively Accepted"));
                    break;
                case ICAL_PARTSTAT_DECLINED:
                    itip_view_set_status (view, _("Declined"));
                    break;
                case ICAL_PARTSTAT_DELEGATED:
                    itip_view_set_status (view, _("Delegated"));
                    break;
                default:
                    itip_view_set_status (view, _("Unknown"));
            }
        }
        e_cal_component_free_attendee_list (list);
    }

    if (info->method == ICAL_METHOD_REPLY
        || info->method == ICAL_METHOD_COUNTER
        || info->method == ICAL_METHOD_DECLINECOUNTER) {
                /* FIXME Check spec to see if multiple comments are actually valid */
                /* Comments for iTIP are limited to one per object */
        e_cal_component_get_comment_list (info->comp, &list);
        if (list) {
            ECalComponentText *text = list->data;

            if (text->value) {
                gchar *html;

                html = camel_text_to_html (
                    text->value,
                    CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
                    CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
                    CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES,
                    0);

                itip_view_set_comment (view, html);

                g_free (html);
            }
        }
        e_cal_component_free_text_list (list);
    }

    e_cal_component_get_description_list (info->comp, &list);
    for (l = list; l; l = l->next) {
        ECalComponentText *text = l->data;

        if (!gstring && text->value)
            gstring = g_string_new (text->value);
        else if (text->value)
            g_string_append_printf (gstring, "\n\n%s", text->value);
    }

    e_cal_component_free_text_list (list);

    if (gstring) {
        gchar *html;

        html = camel_text_to_html (
            gstring->str,
            CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
            CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
            CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
            CAMEL_MIME_FILTER_TOHTML_MARK_CITATION |
            CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES,
            0);

        itip_view_set_description (view, html);
        g_string_free (gstring, TRUE);
        g_free (html);
    }

    settings = g_settings_new ("org.gnome.evolution.calendar");

    if (g_settings_get_boolean (settings, "use-system-timezone"))
        to_zone = e_cal_util_get_system_timezone ();
    else {
        gchar *location;

        location = g_settings_get_string (settings, "timezone");
        if (location != NULL) {
            to_zone = icaltimezone_get_builtin_timezone (location);
            g_free (location);
        }
    }

    if (to_zone == NULL)
        to_zone = icaltimezone_get_utc_timezone ();

    g_object_unref (settings);

    e_cal_component_get_dtstart (info->comp, &datetime);
    info->start_time = 0;
    if (datetime.value) {
        struct tm start_tm;

                /* If the timezone is not in the component, guess the local time */
                /* Should we guess if the timezone is an olsen name somehow? */
        if (datetime.value->is_utc)
            from_zone = icaltimezone_get_utc_timezone ();
        else if (!datetime.value->is_utc && datetime.tzid)
            from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid);
        else
            from_zone = NULL;

        start_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone);

        itip_view_set_start (view, &start_tm, datetime.value->is_date);
        info->start_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
    }

    icalcomp = e_cal_component_get_icalcomponent (info->comp);

        /* Set the recurrence id */
    if (check_is_instance (icalcomp) && datetime.value) {
        ECalComponentRange *recur_id;
        struct icaltimetype icaltime = icaltime_convert_to_zone (*datetime.value, to_zone);

        recur_id = g_new0 (ECalComponentRange, 1);
        recur_id->type = E_CAL_COMPONENT_RANGE_SINGLE;
        recur_id->datetime.value = &icaltime;
        recur_id->datetime.tzid = icaltimezone_get_tzid (to_zone);
        e_cal_component_set_recurid (info->comp, recur_id);
        g_free (recur_id); /* it's ok to call g_free here */
    }
    e_cal_component_free_datetime (&datetime);

    e_cal_component_get_dtend (info->comp, &datetime);
    info->end_time = 0;
    if (datetime.value) {
        struct tm end_tm;

                /* If the timezone is not in the component, guess the local time */
                /* Should we guess if the timezone is an olsen name somehow? */
        if (datetime.value->is_utc)
            from_zone = icaltimezone_get_utc_timezone ();
        else if (!datetime.value->is_utc && datetime.tzid)
            from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid);
        else
            from_zone = NULL;

        if (datetime.value->is_date) {
                        /* RFC says the DTEND is not inclusive, thus subtract one day
                         * if we have a date */

            icaltime_adjust (datetime.value, -1, 0, 0, 0);
        }

        end_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone);

        itip_view_set_end (view, &end_tm, datetime.value->is_date);
        info->end_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
    }
    e_cal_component_free_datetime (&datetime);

        /* Recurrence info */
        /* FIXME Better recurring description */
    if (e_cal_component_has_recurrences (info->comp)) {
                /* FIXME Tell the user we don't support recurring tasks */
        switch (info->type) {
            case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
                itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This meeting recurs"));
                break;
            case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
                itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This task recurs"));
                break;
            case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
                itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This memo recurs"));
                break;
            default:
                g_assert_not_reached ();
                break;
        }
    }

    g_signal_connect (
        view, "response",
        G_CALLBACK (view_response_cb), info);

    if (response_enabled) {
        itip_view_set_show_free_time_check (view, info->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS && (info->method == ICAL_METHOD_PUBLISH || info->method ==  ICAL_METHOD_REQUEST));

        if (info->calendar_uid) {
            start_calendar_server_by_uid (info, view, info->calendar_uid, info->type);
        } else {
            find_server (info, view, info->comp);
            set_buttons_sensitive (info, view);
        }
    } else if (view->priv->dom_document) {
        /* The Open Calendar button can be shown, thus enable it */
        WebKitDOMElement *el;

        el = webkit_dom_document_get_element_by_id (
            view->priv->dom_document, BUTTON_OPEN_CALENDAR);
        webkit_dom_html_button_element_set_disabled (
            WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE);
    }
}