summaryrefslogblamecommitdiffstats
path: root/mbbsd/bbs.c
blob: efac93c4844f077a33a9834c9ecd3998c76cad94 (plain) (tree)
1
2
3
4
5
6
7
8
9
 
                
 
                          
 
                     

                        
                             
 

                          
                                                                       

                                                                   
                                                                                             
 

                                                                




                                                              

                 
                
                  
 

                                     
                                                                      

  
             
                                        

                                  
      
 
                              
                                       
   


                                                                                  











                                                                                 
                             

                                                                       
                                            



                                                                    
                                                                               




                            









































                                                            

                                                                          










                                       



















                                        
                            
                

                      
  





                                        



                                                               
    
                   
 
                                              


                                                   
                                            

                                                       
                     
                                           
                                                      
                                                    
                                     
                                                         
                         


            
                   
   
                     

                                    
                        

                            
            
                         
 
                                               


                                 
 
                                                                       

                                                                 

                 
                   
                                                   

                                                              
                                                                             
                                                                         
                           


                      
               
                                                    
                                                   


                                                                        
 






                                                                       


                      
 
                             
                   


                                                  

                                                                      


                                                                
 
                                    
                         
                                         

                                                                 
 
                                   
                           
            


             

                                        
    
               

                       

                            
                            

                                               
                 
     
 
                                  

                                        
                                                                        

                                 

                                   


                                 



                                                    

                                                            




                               
                                                        




                                          
                              












                                                                         

                                                             
                                                                       

                                                                

                                                                        
 
 
                      

                                        
                                      
                 
                                         



                 


                                                               

                   
 



                                                                     


                               
 






                                             








                                                                    
                                                                      













                                                  
                                     


                                         
                                     






                                  

 

                                 
                      
                                
                 
                                        



                                         
                        

                        
                                                                                   
                 



                                                                              
                 

             
                                                                      
                 
      



             
           
               
 

                       
                     
 
                                                
                            
 




                                       
                                 
                                                                                             
               
 
                   
                                      
      
     
                                                                               
     
 
                                                                         

                                                                                           

 
           
                                      
 



                                                    
                                     
 






                                                                   




                                                          
     










                                                          

     
                    


                                  
                                                                 
     
                                         

                                                                                  
     

















                                                                   
                       
     







                                                     



                        



                               






                                          
 
                                                                       

                                  
                                 


                                 
 














                                                                       
                                      
     
 
                                                        
                    


                                        
                                        
                               
                             
                                                
                             
                                                
                                              
                                

                                                 
                               
 
                        
                                    
                                                       
        

                                                                 
                                                   

                           
 
                                                     
                                   
 
                        
     

                                      

                           
                              


                         
                
                                                  
                                                                   
     
                                    
      





                                     
           
                                         
                                                            
                                                                          
        
                                                         
                                   

 
   
              
 
                                           
                         
                      
 
             
                 
 
               
               

                                        
              
                                                                                        
                                           





                                                                   



                      
 
 
          
               

                              
 
                        

               
                                           
 
                              
                          








                                                           
    
                                                                                               



                                              
                                            
                                                                  



                    
          
                                                            




                                
                                               
                                               
                             
 
                                    
                                           
                                  
                                           
 
                                                    
                               
                                        
        






                                                             
                                                         
                      
             
         
                                                      
                      
                                                                               
 

                                                                          
                                                   

                         
                    

                                                                 
                                   

                                                           
                                
     
               

 
           
                                                        
 
                                      
                       


                       
                             
                                
 

                         
 
                        



                                        



                                                                       

                          
                                                              
                                                           


                                                             
                       
                     



           
                                                    


                                       
                                                                   
                                                                       
     
                  
                                                              
                       




                                         
                                       


        
                                           



     
                                  

                      
                  
                                         


                       
                              

                                         
                                                               
                                         
                      

                                                     

                        






                                                             
                                                         





                                                  


                                                           



                                             

                                           
      

                                       








                                                       
    
                                                               


                                
 
                                         
                                                  
        


                                                                           
                                                

                                                           
 
 
     

                                                                        

                                
                                               
                       



                                           

                                                   
 

                                                



                                 
        
                                                   
 
                                        
                                      
 
                                                                           
                   
                                
                                    
                         
                                                                 

                                         

     
 
          
                       
 
                             
                                                


                                         

                                    

                                                                   
                       
                                                      


                            
 
                   
                                                
                            
 
                        
                  
                                         
                                         
                                                 

           
                                                 
                           
     

             

                                         
                                                 

                          
                   


                          
      
            
 
                                              
 
                                 
                                      
     
                

                                                          

                                     
 
                      
                                                  
          
                                  
















                                                       
         

                                                               
                                                           
     
                              
                          
 
                           
                         
                                                    
                          
                                                                     






                                
                                                          


                                                               
 
                                  
                                                     








                                                                        
    

                                                              



                          
                                                    


                      


                                                                             
           
                               
                          
                       
          
                             
     
                         
      
 



                                            
                               
                               
      
 


                                                                                
     
                  

     
                                       
                    
                  
                                            
                                                
     
        

                             
                                         
                                     
     


                                          

                                                                
                                                
                                        
 
                            
 

                                                       
                                                                         



                                     





                                                              
                         
                              








                                                               

                           
                                    
                                                                   
                                                      


                                                                      
                                                                               
         
                                                          
 

                                                       
                                                                

                                                                       
                                               
                                                                      
                        
                                                                         
         
                               
 


                                                    
                          
             
                                   
                                 
                            
                                                                  
                                               



                                                   


             
                                           
         
 
                              
 
                                   
                                               
 



                                                                           
                                                









                                                 

                                             
                                    
 
                                                                              
                                                                            
                                               
                                                                             
                                  
                    
                                                          
                                                         
                                                                 



                                         


                                  
                                      
                                        
                                                              
                   
                        




                                             
      




                            




                      
   
             

                       
                         
                                                
                            
                                    
                               
                                             
                             



             
                  



                           
           
                                                   
 
                              
    
                                                
 

                                       





                                                                  
                                                     

                                                

                                       

                      
                      










                                                                          
 





                                                                     
                                       
         



                    
   
                                                                 
 
                                                                    

                                 
                                            

                                                                          
     
                     
 
 

          
                                       

                       
                                                             
                         
 





                                                        
                            
          
                                                            
                            

      
                                                
                            
                                    
                                



                                                                                  

     
                                                      
                                                                    
                                
        
                                   

                      

 
          
                                                                      
 
                          

 

                          
                         

                                    
          
                                                                           


             

                            


                                         



                              



                                                  
                                                    






                                    
                                                      
                         



                             
                                                           
 
                              
                                
                             
                                            
                                              

                                 
                                

                            



                                        
 




                                            
                                                      
 


                                        
 
                  
                                         
                                      
                         
 

                                   

                         




                                  






                                                            




                                            

                                                                      

     
                                  
                                                       
 
                        
 




                                                      





                                                                 
                               

                                         
                                

                                                
 
                                                      
                                        



                                   
                        
                                                         
 




                                                            
        

                          
                                                                       


                                        
                                                                     


                                
 
 
                                                                        

                  
                              
 







                                                      
                                                                      









                                                        



                                                                             






                                                      

                                                                    
                                                           





                                                                        
                                                                    
       

                                 

                                                                          








                                 




                                                                             



                                                               
                                                                       




                         
             

                               
                                             
                                         
                                          
 



                                               
                                                          

                                      
 


                              

                                                    
                                                              
                               

                                                



                                                    



                                                   





                           

                           






                                                                   
                              
 
                                       
                                                              
 
                               
         


                                                        
 


                                                        
              
 




                                                     


                      

                                                
          
                                         









                                                                  
                                                            
 
                                                                            
                                                          

                          
                                           
                       
 
                                                
                            
 
                                             




                                                     
 






                                               
                    







                                                              


                                                             
                                                            



                          







                                                                  

               





                                                                           

               
                                                




                             
                                                   
                          
     
 
                                                            
 





                                                                       
 
                        
     
                               


                          

                                    
     
                                                             

















                                                    
                   

                                       
                                     

                          

      
            
               
                                                                        

                                                        
                               

                                                                        
                                                         
                             

         
            
                                                                     
        
                                                     
 
                                                                           
                                              
                            
                                                                       
                                                    
     
 
                                                                      
 

                                               
                                       



                                  
                   
                                                                   
            
                                                                    
                                                          
                               
                                        
         
                                                   

                                  

                                   
                                        
                                   
 
                                 








                                                                    
 

                              
 
                    
                                              
                                   
         
                                            
                                    
                                      
                                      
 

                                                
                                     
             
                                            


                                                  

                                                                
 

                                        

                                              


                                        

                                      
      
                                      
                                                              
                                               

                                               
                                                       

                          
                                                         

              
         



                                                  
         
 
          
                                                                       


                                                    
                                                           
                                                                     
                   
                        




                                             
      
                                   
                                                            


















                                                                     
                      

                             
 

                      
 
          
                                                           
 
                                
                                
 
                                                                                
                         
 
                           
                                               
 
                                    
 




                                   
 
                     

                                     
                                                 
                                                                    




                                                           



                                            
                                           
                                        
                                           
                                        
                                           



                                        
                                                
                                                                
 





                                 
                         
                            

                              
                             

                                         


                                                        


                           
                      
 
 
    
                                                                                   






                             






                                    
                                                                            



                                     
                                    

                                    
                                                                                             






                                         
                                                              





                                          



                        
   
                                                              
 
                     
                                            
 
                                                

                                                                
                         
    
                         
                                                                                  

                                        

                                   
                                         
                       




                                                                                                       

                                         
 
                                                    




                                                                        



                                        

                                         
 
                                                    




                                                                        



                                                             

                                                  
 







                                                 



                                                           
           
 
                                 

                             
                                                             

                                    
                                                             

                                                   
     
                                                                  

                                                   
                        
 

                 
          
                 


                                                       
                                                
                                              
                 






                                                      
                                                    

                                                                        
             

          
                                                                   
 
                                   
                         



                                           
                                                



                      
                 


                                                                                   
                  


                                            
 
                                                

                                 

                               
                                       


                   


                                                      
 






                                                            
                              
                                                        
                                                                            
 
         
                          
     

                                                  
                                                             

                              

                                         
                                                        

                                  


                               
                                                                     







                                                                   
                                     
                          
 

                                   
                                                     
 
                                                     
 
                                

                          
                                                                       



                                                                                  
                                                  
                                                    
                                                                        
     
               
                                    
                                              
                                                             





                                                                    
                        

                                                                           
                               
                                                





                                                 
 
                                                  
                                                      

                                                      
 




                                                     
                                                                          
                                                   




                                                      
                         

                      

      
          
                                                                 
 
                                   









                                               
 
   
                                                            
 
                                     

                                    












                                                    
 



                                                                       

                                                              
     


                    


                                                                           
         
                                                                   
                                                                         
                    
         
     


              

                                                                     









                                                                   
                            
                                                     
         
     
                      
 
 
          
                                                            
 
                                  
                                      
                                                 
                           
                           
     

                     
 

          
                                                                  



                                 
                                                                    



                          
                                             

                      
 
          

                                                        
 
                          
                       






                                                                     
 
       
                                             
                                                    
                         

                  
 

                            
                                                                          
                                                                              
 
                                                                 
                     
                                                                     
                      
                              


                                            
 



                                                        
                      

                                                    
     
    

             
 
   
                                                           
 
                                              
                                         
                                 
                                    
                    
                                              
                                         
                     
                                                   
                                        
                                        
                                        
                                                    


                                        
                                                   

                                  
                                    
                     
      
                                    
                       
                                      

                                                

                                                              
                          
 


                                    
                                                
                            
                                                                     
                                                                             
                               

                          

                                       
                                                                  

                          




                                                                      
 
                          
                                   



                           
 
             

                                       
                                                 



                          
                                                      




                                                          







                                                                     
     
               












                                                                


                           
                   















                                                                       





                             
                        
 








                                                                   
                                               



                                                                       
     
                          
                             
                                    


                                             
                                                 

      

             







                                                            

                    
                             
                                    
                                             

      
 
                    
        
     

              
                                     
                                               
 
                                          
         





                                                              
         
                                   











                                               
 
                                                               


                                                 
                                     
     
      
 




                                          
                                                                         


                             


                                       

                                                            


                                        

                                                             

                                               
                                                          


         

                                       
 










                                                
 

                                                                                       
                                                        





                                                       



                 

                                                         


                                             
 
                   
                              

                                        
 
                      
                              
                     
                               
                                                             


                                              
                       
 

                     
 
                                                          
                          
 
                                   
     
                    








                                                              



                                                                 






                              
                                                                                       



                                                        

                                                                  






                           
                            
 



                               


                                        
                              
                              
                                      

                                        

                                      
         
 
                   



                                                        
                                        
     


                                                                  
                                                   
                                   
                      

     
                                                    
                        
                                
                                                                              

                      
 
          
                                                           
 
                                    



                                 


                                                 

                                                                             

                         
                                  
                                             
                       

                       
 
   
                                                                


                                     
                              

                                                



                                                         
                                
                                           

                             
 

                                                       
                                                

                          
 
                                                           
                                                                

                           
                             

                              
                                                  

                            
                             



                                                     
                        

                                       
                          
                                                                 
                                                                      
                
      



                                                     
                                   

                                                                             
                                                                         



                                                             
 
                                                                                         
                                   
                                      
                                 
 
                              
         
                          
     

                     
 


                                                    
                          
                                             

                                                  
                                                             
                       
 
                                                
                            
 
                                       

                         





                                         

                                                                          


                                

                                                





                                                                             
                                             
                                                  
                                                                      
                                         

                         

                                                      






                                                





                                                                              



                                                                  
                                                                   


                                                            





                                               
                             

                                                                     








                                                                                        
                           

                          
                                                                         
                                                                                  
      


                                                                          
                                                            
              
 
                                                                         
                                               
             
                                                       
 


                                                                                               



                                                                         
                                                                                      


                                        
                                                       

                              







                                                                                 


                                                     
                                                                    

                                     
                          
                                                     
 
                                         
                               


                                                                   
                                                                                      


                                                                          
                                    





                                                                        


                                                                       

                   

                                                                              
      
 
                                                         
                                       



                                                                      
                                         
                                                            
                                                        

                                                           
                                                     
                                                             
                        
                                                          
                                                                      

                      

                                                             
                                        

                                                    
 


                                                                 
                                                                          






                                                                                    

                                               
                       
                      
                                     

                    
             
           
      

                               





                                                                              
                                                                            


                                                                        
                                                        
                  
                                      
 

                                                                                      
             






                                                
                                   
                                                





                                                                                      
                                                           
                                                            
                   

                                                       
      
                 
              
                

                             
                                 
                                            
                                                        
                                                              
                                                           
             
 
                              

                          

                      
 
                               
                                                                     
 
                                                  
                                           


                       
          

                                                           

                              
          



                             
 


                                                                           


                            



                                                   

                                                                        
                                                
                              
                                                 
                                

                           
     
                                      
                                                    
                              
                                
                                                 

                           
     
                                             
                                                                                             







                                                                                         



                      
                                                                                 
 


                                             
                        
                             


                             
 
                                                               
                       



                                                           
 

                                                                          
                                







                                  

                                                 


                                                
                    
 

                                              
                                                                                               

                                           


                    
               

                            

                                                        
                                                               
                        




                                          


        
                     

     
                        
     
















                                                                    


                                                                                            

                                                
                                                                






                                                                           


      
                                       
                                                                             
                              
                                                        
                                                          

                                       
 
                 
                                              
            
                                               


                 
                                                                                               



                          
 
                                          
       
                                   

                     



                                           
                                           
                     





                                                                 
      
 





                      
                  






                                               
                                                                


                                                     
                          
     
                                                                           
                              

                                             

                          












                                                                 

                                                                         
 

                                                                       


                                     

                          
      
                                                                         
                                       
                                                                           
                                     
                               


                                                          
                          
     
                         
                                      









                                                          
      
 
                                                           
                                                             



                                                           
                                 

                                        
                                        
                                         
                             

                        

                         
            


                                           
                                  





























                                                                           
     

             
 
          
                  

                                
                                                    

                                   
     

             
 
          
             
 
                                 
               
 
                                       



                       

                    
                                     
     

                          

                      
 
   
                  
 
                             
                   
                          
                                             
        
                                       

                     
 
   
                  



                               


                                                       
 


                                   
 
 
          
                                                            
 
            
                      

                                                            
                         
                                                                    
                                  




                                                                                                     
                                           

                                             
                          
                                         
                                                          
                       
                                                    
                                
           
                                        

                                      
                                                          

         
                                      

                                                               
                                                
                            
                      
 

          
                                                           







                                                             
                                           

                                                                  
 

                                                         


                                             
      

                                               

              
                                                                                            
                                          
                                          

                              
         

                               
                                          
 


                                              


                                
                      
                                                                       
 

                           
 
                            
                                                                        
                              

                                                    
 
                
                                                    


                                                                            
                                                          
         
      

                                                                       


                                             
      

                                               

              
                                                                                           
                                          
                                          

                             

         
                                             
                      

 
          
            
 
                                

                      
 
                   



                                              
                                                   


                                                     
                                                                   


                                      
                                                                    

                  
                                        
       
                                                                            


                    




                                                                        
                                                                           
                                    


                     



                       



                                       
                     
 
                              

                                           
                                                   
                                                        


                                     
                                                      



                                           
                                                                        
                                                          



                                    
                                                
                                                                    
                                                                                  




                      


                   

                                                           
                                                   


                          

 


                                                           
                                                                      
                               



                                       
                                  
                             
                
                             
     
                                    
      



                                               
                             
                             
                             
                                                                  







                                     
                                    
                             
                                                  

                                                                    

                                                       





                                                                    
                           




                               
                                    
                           
                   
                                  
     
                       
      
                       
                             
                       
                       
                       
                       
                                








                                   


                                                        




                            
                
                       
     
                              
      

                            
                           




                            
                       




                            
                  
                               
     
                       
      
                       



                             
  
 
   
          


                                                                
                                 
 
                                                                 

                               
 
                         
 

                                              
               
 






                                           
                            
                                                                 
                    
                                  





                           
 
   
                


                                           
                                
 
                      
                                 
     
               

                    





                                                             

                           
                   
 
 
   
            
 
                       
 
 

#include "bbs.h"

#ifdef EDITPOST_SMARTMERGE

#include "fnv_hash.h"
#define SMHASHLEN (64/8)

#endif // EDITPOST_SMARTMERGE

#define WHEREAMI_LEVEL  16

static int recommend(int ent, fileheader_t * fhdr, const char *direct);
static int do_add_recommend(const char *direct, fileheader_t *fhdr,
         int ent, const char *buf, int type);
static int view_postinfo(int ent, const fileheader_t * fhdr, const char *direct, int crs_ln);

static int bnote_lastbid = -1; // 決定是否要顯示進板畫面的 cache

// recommend/comment types
//  most people use recommendation just for one-line reply. 
//  so we may change default to (RECTYPE_ARROW)= comment only.
//  however, the traditional behavior (which does not have
//  BRC info for 'new comments available') uses RECTYPE_GOOD.
enum {
    RECTYPE_GOOD,
    RECTYPE_BAD,
    RECTYPE_ARROW,

    RECTYPE_SIZE,
    RECTYPE_MAX     = RECTYPE_SIZE-1,
    RECTYPE_DEFAULT = RECTYPE_GOOD, // match traditional user behavior
};

#ifdef ASSESS
static char * const badpost_reason[] = {
    "廣告", "不當用辭", "人身攻擊"
};
#endif

/* TODO multi.money is a mess.
 * please help verify and finish these.
 */
/* modes to invalid multi.money */
#define INVALIDMONEY_MODES (FILE_ANONYMOUS | FILE_BOTTOM | FILE_DIGEST | FILE_BID)

/* query money by fileheader pointer.
 * return <0 if money is not valid.
 */
int 
query_file_money(const fileheader_t *pfh)
{
    fileheader_t hdr;

    if( (currmode & MODE_SELECT) &&  
    (pfh->multi.refer.flag) &&
    (pfh->multi.refer.ref > 0)) // really? not sure, copied from other's code
    {
    char genbuf[PATHLEN];

    /* it is assumed that in MODE_SELECT, currboard is selected. */
    setbfile(genbuf, currboard, FN_DIR);
    get_record(genbuf, &hdr, sizeof(hdr), pfh->multi.refer.ref);
    pfh = &hdr;
    }

    if(pfh->filemode & INVALIDMONEY_MODES || pfh->multi.money > MAX_POST_MONEY)
    return -1;

    return pfh->multi.money;
}

// lite weight version to update dir files
static int 
modify_dir_lite(
    const char *direct, int ent, const char *fhdr_name,
    time4_t modified, const char *title, char recommend)
{
    // since we want to do 'modification'...
    int fd;
    off_t sz = dashs(direct);
    fileheader_t fhdr;

    // TODO lock?
    // PttLock(fd, offset, size, F_WRLCK);
    // write(fd, rptr, size);
    // PttLock(fd, offset, size, F_UNLCK);
    
    // prevent black holes
    if (sz < sizeof(fileheader_t) * (ent) ||
        (fd = open(direct, O_RDWR)) < 0 )
    return -1;

    // also check if fhdr->filename is same.
    // let sz = base offset
    sz = (sizeof(fileheader_t) * (ent-1));
    if (lseek(fd, sz, SEEK_SET) < 0 ||
    read(fd, &fhdr, sizeof(fhdr)) != sizeof(fhdr) ||
    strcmp(fhdr.filename, fhdr_name) != 0)
    {
    close(fd);
    return -1;
    }

    // update records
    if (modified > 0)
    fhdr.modified = modified;

    if (title && *title)
    strcpy(fhdr.title, title);

    if (recommend) 
    {
    recommend += fhdr.recommend;
    if (recommend > MAX_RECOMMENDS) recommend = MAX_RECOMMENDS;
    else if (recommend < -MAX_RECOMMENDS) recommend = -MAX_RECOMMENDS;
    fhdr.recommend = recommend;
    }

    if (lseek(fd, sz, SEEK_SET) >= 0)
    write(fd, &fhdr, sizeof(fhdr));

    close(fd);

    return 0;
}

static void 
check_locked(fileheader_t *fhdr)
{
    boardheader_t *bp = NULL;

    if (currstat == RMAIL)
    return;
    if (!currboard[0] || currbid <= 0)
    return;
    bp = getbcache(currbid);
    if (!bp)
    return;
    if (!(fhdr->filemode & FILE_SOLVED))
    return;
    if (!(fhdr->filemode & FILE_MARKED))
    return;
    syncnow();
    bp->SRexpire = now;
}

/* hack for listing modes */
enum LISTMODES {
    LISTMODE_DATE = 0,
    LISTMODE_MONEY,
};
static char *listmode_desc[] = {
    "日 期",
    "價 格",
};
static int currlistmode = LISTMODE_DATE;

#define IS_LISTING_MONEY \
    (currlistmode == LISTMODE_MONEY || \
     ((currmode & MODE_SELECT) && (currsrmode & RS_MONEY)))

void
anticrosspost(void)
{
    log_filef("etc/illegal_money",  LOG_CREAT,
             ANSI_COLOR(1;33;46) "%s "
             ANSI_COLOR(37;45) "cross post 文章 "
             ANSI_COLOR(37) " %s" ANSI_RESET "\n", 
             cuser.userid, Cdatelite(&now));
    post_violatelaw(cuser.userid, BBSMNAME "系統警察", 
        "Cross-post", "罰單處份");
    pwcuViolateLaw();
    mail_id(cuser.userid, "Cross-Post罰單",
        "etc/crosspost.txt", BBSMNAME "警察部隊");
    if ((now - cuser.firstlogin) / DAY_SECONDS < 14)
    delete_allpost(cuser.userid);
    kick_all(cuser.userid); // XXX: in2: wait for testing
    u_exit("Cross Post");
    exit(0);
}

/* Heat CharlieL */
int
save_violatelaw(void)
{
    char            buf[128], ok[3];
    int             day;

    setutmpmode(VIOLATELAW);
    clear();
    vs_hdr("繳罰單中心");

    if (!(cuser.userlevel & PERM_VIOLATELAW)) {
    vmsg("你沒有被開罰單~~");
    return 0;
    }

    day =  cuser.vl_count*3 - (now - cuser.timeviolatelaw)/DAY_SECONDS;
    if (day > 0) {
        vmsgf("依照違規次數, 你還需要反省 %d 天才能繳罰單", day);
    return 0;
    }
    reload_money();
    if (cuser.money < (int)cuser.vl_count * 1000) {
    snprintf(buf, sizeof(buf),
         ANSI_COLOR(1;31) "這是你第 %d 次違反本站法規"
         "必須繳出 %d 元;但你目前只有 %d 元, 錢不夠啦!!" ANSI_RESET,
           (int)cuser.vl_count, (int)cuser.vl_count * 1000, cuser.money);
    mvouts(22, 0, buf);
    pressanykey();
    return 0;
    }
    move(5, 0);
    prints("這是你第 %d 次違法 必須繳出 %d 元\n\n", 
        cuser.vl_count, cuser.vl_count * 1000);
    outs(ANSI_COLOR(1;37) "你知道嗎? 因為你的違法 "
       "已經造成很多人的不便" ANSI_RESET "\n");
    outs(ANSI_COLOR(1;37) "你是否確定以後不會再犯了?" ANSI_RESET "\n");

    if (!getdata(10, 0, "確定嗎?[y/N]:", ok, sizeof(ok), LCECHO) ||
    ok[0] != 'y') 
    {
    move(15, 0);
    outs( ANSI_COLOR(1;31) "不想付錢嗎? 還是不知道要按 y ?\n"
        "請養成看清楚系統訊息的好習慣。\n"
        "等你想通了再來吧!! 我相信你不會知錯不改的~~~" ANSI_RESET);
    pressanykey();
    return 0;
    }

    //Ptt:check one more time
    reload_money();
    if (cuser.money < (int)cuser.vl_count * 1000) 
    {
    log_filef("log/violation", LOG_CREAT,
        "%s %s pay-violation error: race-conditionn hack?\n", 
        Cdate(&now), cuser.userid);
    vmsg("錢怎麼忽然不夠了? 試圖欺騙系統被查到將砍帳號!");
    return 0; 
    }

    demoney(-1000 * cuser.vl_count);
    pwcuSaveViolateLaw();
    log_filef("log/violation", LOG_CREAT,
        "%s %s pay-violation: $%d complete.\n", 
        Cdate(&now), cuser.userid, (int)cuser.vl_count*1000);

    vmsg("罰單已付,請重新登入。");
    u_exit("save_violate");
    exit(0);
    return 0;
}

static time4_t  *board_note_time = NULL;

void
set_board(void)
{
    boardheader_t  *bp;

    bp = getbcache(currbid);
    if( !HasBoardPerm(bp) ){
    vmsg("access control violation, exit");
    u_exit("access control violation!");
    exit(-1);
    }

    if( HasUserPerm(PERM_SYSOP) &&
    (bp->brdattr & BRD_HIDE) &&
    !is_BM_cache(bp - bcache + 1) &&
    !is_hidden_board_friend((int)(bp - bcache) + 1, currutmp->uid) )
    vmsg("進入未經授權看板");

    board_note_time = &bp->bupdate;

    if(bp->BM[0] <= ' ')
    strcpy(currBM, "徵求中");
    else
    {
    /* calculate with other title information */
    int l = 0;

    snprintf(currBM, sizeof(currBM), "板主:%s", bp->BM);
    /* title has +7 leading symbols */
    l += strlen(bp->title);
    if(l >= 7) 
        l -= 7;
    else 
        l = 0;
    l += 8 + strlen(currboard); /* trailing stuff */
    l += strlen(bp->brdname);
    l = t_columns - l -strlen(currBM);

#ifdef _DEBUG
    {
        char buf[PATHLEN];
        sprintf(buf, "title=%d, brdname=%d, currBM=%d, t_c=%d, l=%d",
            strlen(bp->title), strlen(bp->brdname),
            strlen(currBM), t_columns, l);
        vmsg(buf);
    }
#endif

    if(l < 0 && ((l += strlen(currBM)) > 7))
    {
        currBM[l] = 0;
        currBM[l-1] = currBM[l-2] = '.';
    }
    }

    /* init basic perm, but post perm is checked on demand */
    currmode = (currmode & (MODE_DIRTY | MODE_GROUPOP)) | MODE_STARTED;
    if (!HasUserPerm(PERM_NOCITIZEN) && 
         (HasUserPerm(PERM_ALLBOARD) || is_BM_cache(currbid))) {
    currmode = currmode | MODE_BOARD | MODE_POST | MODE_POSTCHECKED;
    }
}

// post 文章不算錢的板
int IsFreeBoardName(const char *brdname)
{
    if (strcmp(brdname, BN_TEST) == 0)
    return 1;
    if (strcmp(brdname, BN_ALLPOST) == 0)
    return 1;
    return 0;
}

/* check post perm on demand, no double checks in current board
 * currboard MUST be defined!
 * XXX can we replace currboard with currbid ? */
int
CheckPostPerm(void)
{
    static time4_t last_chk_time = 0x0BAD0BB5; /* any magic number */
    static int last_board_index = 0; /* for speed up */
    int valid_index = 0;
    boardheader_t *bp = NULL;
    
    if (currmode & MODE_DIGEST)
    return 0;

    // check if my own permission is changed.
    if (ISNEWPERM(currutmp))
    {
    currmode &= ~MODE_POSTCHECKED;
    pwcuReload();
    }

    if (currmode & MODE_POSTCHECKED)
    {
    /* checked? let's check if perm reloaded */
    if (last_board_index < 1 || last_board_index > SHM->Bnumber)
    {
        /* invalid board index, refetch. */
        last_board_index = getbnum(currboard);
        valid_index = 1;
    }
    assert(0<=last_board_index-1 && last_board_index-1<MAX_BOARD);
    bp = getbcache(last_board_index);

    if(bp->perm_reload != last_chk_time)
        currmode &= ~MODE_POSTCHECKED;
    }

    if (!(currmode & MODE_POSTCHECKED))
    {
    if(!valid_index)
    {
        last_board_index = getbnum(currboard);
        bp = getbcache(last_board_index);
    }
    last_chk_time = bp->perm_reload;
    currmode |= MODE_POSTCHECKED;

    // vmsg("reload board postperm");
    
    if (haspostperm(currboard)) {
        currmode |= MODE_POST;
        return 1;
    }
    currmode &= ~MODE_POST;
    return 0;
    }
    return (currmode & MODE_POST);
}

int CheckPostRestriction(int bid)
{
    boardheader_t *bp;
    if (HasUserPerm(PERM_SYSOP))
    return 1;
    assert(0<=bid-1 && bid-1<MAX_BOARD);

    // XXX currmode 是目前看板不是 bid...
    if (is_BM_cache(bid))
    return 1;
    bp = getbcache(bid);

    // check first-login
    if (cuser.firstlogin > (now - (time4_t)bp->post_limit_regtime * MONTH_SECONDS))
    return 0;
    // XXX numposts itself is an integer, but some records (by del post!?) may
    // create invalid records as -1... so we'd better make it signed for real
    // comparison.
    if ((int)(cuser.numposts  / 10) < (int)(unsigned int)bp->post_limit_posts)
    return 0;

#ifdef ASSESS
    if  (cuser.badpost > (255 - (unsigned int)bp->post_limit_badpost))
    return 0;
#endif

    return 1;
}

static void
readtitle(void)
{
    boardheader_t  *bp;
    char    *brd_title;
    char     buf[32];

    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    bp = getbcache(currbid);

    if(bp->bvote != 2 && bp->bvote)
    brd_title = "本看板進行投票中";
    else
    brd_title = bp->title + 7;

    showtitle(currBM, brd_title);
    outs("[←]離開 [→]閱\讀 [Ctrl-P]發表文章 [d]刪除 [z]精華區 [i]看板資訊/設定 [h]說明\n");
    buf[0] = 0;

#ifdef USE_COOLDOWN
    if (!(bp->brdattr & BRD_COOLDOWN))
#endif
    {
    snprintf(buf, sizeof(buf), "人氣:%d ", SHM->bcache[currbid - 1].nuser);
    }

    vbarf(ANSI_REVERSE "   編號    %s 作  者       文  章  標  題\t%s ", 
        IS_LISTING_MONEY ? listmode_desc[LISTMODE_MONEY] : listmode_desc[currlistmode],
        buf);
}

static void
readdoent(int num, fileheader_t * ent)
{
    int  type = ' ';
    char *mark, *title,
     color, special = 0, isonline = 0, recom[8];
    char *typeattr = "";
    char isunread = 0, oisunread = 0;

#ifdef DETECT_SAFEDEL
    char iscorpse = (ent->owner[0] == '-') && (ent->owner[1] == 0);

    if (!iscorpse)
    {
#endif

    oisunread = isunread = 
    brc_unread(currbid, ent->filename, ent->modified);

    // modified tag
    if (isunread == 2)
    {
    // ignore unread, if user doesn't want to show it.
    if (cuser.uflag & NO_MODMARK_FLAG)
    {
        oisunread = isunread = 0;
    }
    // if user wants colored marks, use 'read' marks
    else if (cuser.uflag & COLORED_MODMARK)
    {
        isunread = 0;
        typeattr = ANSI_COLOR(36);
    }
    }

    // handle 'type'
    type = isunread ? '+' : ' ';
    if (isunread == 2) type = '~';

    if ((currmode & MODE_BOARD) && (ent->filemode & FILE_DIGEST))
    {
    type = (type == ' ') ? '*' : '#';
    }
    else if (ent->filemode & FILE_MARKED) // marks should be visible to everyone. 
    {
    if(ent->filemode & FILE_SOLVED)
        type = '!';
    else if (isunread == 0)
        type = 'm';
    else if (isunread == 1)
        type = 'M';
    else if (isunread == 2)
        type = '=';
    }
    else if (ent->filemode & FILE_SOLVED)
    {
    type = (type == ' ') ? 's': 'S';
    }

    // tag should override everything
    if ((currmode & MODE_BOARD) || HasUserPerm(PERM_LOGINOK)) 
    {
    if (TagNum && !Tagger(atoi(ent->filename + 2), 0, TAG_NIN))
        type = 'D';
    }

    // the only special case: ' ' with isunread == 2,
    // change to '+' with gray attribute.
    if (type == ' ' && oisunread == 2)
    {
    typeattr = ANSI_COLOR(1;30);
    type = '+';
    }
#ifdef DETECT_SAFEDEL
    } // if(!iscorpse)
    else {
    // quick display
#ifdef USE_PFTERM
    outs(ANSI_COLOR(1;30));
#endif
    prints("%7d    ", num);
    prints("%-6.5s", ent->date);
    prints("%-13.12s", ent->owner);
    prints("╳ %-.*s" ANSI_RESET "\n",
        t_columns-34, ent->title);
    return;
    }
#endif

    title = ent->filename[0]!='L' ? subject(ent->title) : "<本文鎖定>";
    if (ent->filemode & FILE_VOTE)
    color = '2', mark = "ˇ";
    else if (title == ent->title)
    color = '1', mark = "□";
    else
    color = '3', mark = "R:";

    /* 把過長的 title 砍掉。 前面約有 33 個字元。 */
    {
    int l = t_columns - 34; /* 33+1, for trailing one more space */
    unsigned char *p = (unsigned char*)title;

    /* strlen 順便做 safe print checking */
    while (*p && l > 0)
    {
        /* 本來應該做 DBCS checking, 懶得寫了 */
        if(*p < ' ')
        *p = ' ';
        p++, l--;
    }

    if (*p && l <= 0)
        strcpy((char*)p-3, " …");
    }

    if (title[0] == '[' && !strncmp(title, "[公告]", 6))
    special = 1;

    isonline = query_online(ent->owner);

    if(ent->recommend >= MAX_RECOMMENDS)
      strcpy(recom,"1m爆");
    else if(ent->recommend>9)
      sprintf(recom,"3m%2d",ent->recommend);
    else if(ent->recommend>0)
      sprintf(recom,"2m%2d",ent->recommend);
    else if(ent->recommend <= -MAX_RECOMMENDS)
      sprintf(recom,"0mXX");
    else if(ent->recommend<-10)
      sprintf(recom,"0mX%d",-ent->recommend);
    else strcpy(recom,"0m  "); 

    /* start printing */
    if (ent->filemode & FILE_BOTTOM)
    outs("  " ANSI_COLOR(1;33) "  ★ " ANSI_RESET);
    else
    /* recently we found that many boards have >10k articles,
     * so it's better to use 5+2 (2 for cursor marker) here.
     * XXX if we are in big term, enlarge here.
     */
    prints("%7d", num);

    prints(" %s%c" ESC_STR "[0;1;3%4.4s" ANSI_RESET, 
        typeattr, type, recom);

    if(IS_LISTING_MONEY)
    {
    int m = query_file_money(ent);
    if(m < 0)
        outs(" ---- ");
    else
        prints("%5d ", m);
    }
    else // LISTMODE_DATE
    {
#ifdef COLORDATE
    prints(ANSI_COLOR(%d) "%-6.5s" ANSI_RESET,
        (ent->date[3] + ent->date[4]) % 7 + 31, ent->date);
#else
    prints("%-6.5s", ent->date);
#endif
    }

    // print author
    if(isonline) outs(ANSI_COLOR(1));
    prints("%-13.12s", ent->owner);
    if(isonline) outs(ANSI_RESET);
       
    if (strncmp(currtitle, title, TTLEN))
    prints("%s " ANSI_COLOR(1) "%.*s" ANSI_RESET "%s\n",
           mark, special ? 6 : 0, title, special ? title + 6 : title);
    else
    prints(ANSI_COLOR(1;3%c) "%s %s" ANSI_RESET "\n",
           color, mark, title);
}

int
whereami(void)
{
    boardheader_t  *bh, *p[WHEREAMI_LEVEL];
    int             i, j;
    int bid = currbid;

    if (!bid)
    return 0;

    move(1, 0);
    clrtobot();
    assert(0<=bid-1 && bid-1<MAX_BOARD);
    bh = getbcache(bid);
    p[0] = bh;
    for (i = 0; i+1 < WHEREAMI_LEVEL && p[i]->parent>1 && p[i]->parent < numboards; i++)
    p[i + 1] = getbcache(p[i]->parent);
    j = i;
    prints("我在哪?\n%-40.40s %.13s\n", p[j]->title + 7, p[j]->BM);
    for (j--; j >= 0; j--)
    prints("%*s %-13.13s %-37.37s %.13s\n", (i - j) * 2, "",
           p[j]->brdname, p[j]->title,
           p[j]->BM);

    pressanykey();
    return FULLUPDATE;
}


static int
do_select(void)
{
    char            bname[20];

    setutmpmode(SELECT);
    move(0, 0);
    clrtoeol();
    CompleteBoard(MSG_SELECT_BOARD, bname);

    if(enter_board(bname) < 0)
    return FULLUPDATE;

    move(1, 0);
    clrtoeol();
    return NEWDIRECT;
}

/* ----------------------------------------------------- */
/* 改良 innbbsd 轉出信件、連線砍信之處理程序             */
/* ----------------------------------------------------- */
void
outgo_post(const fileheader_t *fh, const char *board, const char *userid, const char *nickname)
{
    FILE           *foo;

    if ((foo = fopen("innd/out.bntp", "a"))) {
    fprintf(foo, "%s\t%s\t%s\t%s\t%s\n",
        board, fh->filename, userid, nickname, fh->title);
    fclose(foo);
    }
}

static int
cancelpost(const fileheader_t *fh, int by_BM, char *newpath)
{
    FILE           *fin, *fout;
    char           *ptr, *brd;
    fileheader_t    postfile;
    char            genbuf[200];
    char            nick[STRLEN], fn1[PATHLEN];
    int             len = 42-strlen(currboard);
    int         ret = -1;

    if(!fh->filename[0]) return ret;
    setbfile(fn1, currboard, fh->filename);
    if ((fin = fopen(fn1, "r"))) {
    brd = by_BM ? BN_DELETED : BN_JUNK;

        memcpy(&postfile, fh, sizeof(fileheader_t));
    setbpath(newpath, brd);
    stampfile_u(newpath, &postfile);
    
    nick[0] = '\0';
    while (fgets(genbuf, sizeof(genbuf), fin)) {
        if (!strncmp(genbuf, str_author1, LEN_AUTHOR1) ||
        !strncmp(genbuf, str_author2, LEN_AUTHOR2)) {
        if ((ptr = strrchr(genbuf, ')')))
            *ptr = '\0';
        if ((ptr = (char *)strchr(genbuf, '(')))
            strlcpy(nick, ptr + 1, sizeof(nick));
        break;
        }
    }
    if(!strncasecmp(postfile.title, str_reply, 3))
        len=len+4;
    sprintf(postfile.title, "%-*.*s.%s板", len, len, fh->title, currboard);

    if ((fout = fopen("innd/cancel.bntp", "a"))) {
        fprintf(fout, "%s\t%s\t%s\t%s\t%s\n", currboard, fh->filename,
            cuser.userid, nick, fh->title);
        fclose(fout);
    }
    fclose(fin);
        log_filef(fn1,  LOG_CREAT, "\n※ Deleted by: %s (%s) %s",
                 cuser.userid, fromhost, Cdatelite(&now));
    ret = Rename(fn1, newpath);
    setbdir(genbuf, brd);
    append_record(genbuf, &postfile, sizeof(postfile));
    setbtotal(getbnum(brd));
    }
    return ret;
}

static void
do_deleteCrossPost(const fileheader_t *fh, char bname[])
{
    char bdir[PATHLEN], file[PATHLEN];
    fileheader_t newfh;
    boardheader_t  *bp;
    int i, bid;

    if(!bname || !fh) return;
    if(!fh->filename[0]) return;

    bid = getbnum(bname);
    if(bid <= 0) return;

    bp = getbcache(bid);
    if(!bp) return;

    setbdir(bdir, bname);
    setbfile(file, bname, fh->filename);
    memcpy(&newfh, fh, sizeof(fileheader_t)); 
    // Ptt: protect original fh 
    // because getindex safe_article_delete will change fh in some case
    if( (i=getindex(bdir, &newfh, 0))>0)
    {
#ifdef SAFE_ARTICLE_DELETE
        if(bp && !(currmode & MODE_DIGEST) && bp->nuser > 30 )
            safe_article_delete(i, &newfh, bdir, NULL);
        else
#endif
                delete_record(bdir, sizeof(fileheader_t), i);
    setbtotal(bid);
    unlink(file);
    }
}

static void
deleteCrossPost(const fileheader_t *fh, char *bname)
{
    if(!fh || !fh->filename[0]) return;

    if(!strcmp(bname, BN_ALLPOST) || !strcmp(bname, "NEWIDPOST") ||
       !strcmp(bname, BN_ALLHIDPOST) || !strcmp(bname, BN_UNANONYMOUS))
    {
        int len=0;
    char xbname[TTLEN + 1], *po = strrchr(fh->title, '.');
    if(!po) return;
    po++;
        len = (int) strlen(po)-2;

    if(len > TTLEN) return;
    sprintf(xbname, "%.*s", len, po);
    do_deleteCrossPost(fh, xbname);
    }
    else
    {
    do_deleteCrossPost(fh, BN_ALLPOST);
    }
}

void
delete_allpost(const char *userid)
{
    fileheader_t fhdr;
    int     fd, i;
    char    bdir[PATHLEN], file[PATHLEN];

    if(!userid) return;

    setbdir(bdir, BN_ALLPOST);
    if( (fd = open(bdir, O_RDWR)) != -1) 
    {
       for(i=0; read(fd, &fhdr, sizeof(fileheader_t)) >0; i++){
           if(strcmp(fhdr.owner, userid))
             continue;
           deleteCrossPost(&fhdr, BN_ALLPOST);
       setbfile(file, BN_ALLPOST, fhdr.filename);
       unlink(file);

       // usually delete_allpost are initiated by system,
       // so don't set normal safedel.
       strcpy(fhdr.filename, FN_SAFEDEL);
       strcpy(fhdr.owner, "-");
       snprintf(fhdr.title, sizeof(fhdr.title),
           "%s", STR_SAFEDEL_TITLE);

           lseek(fd, sizeof(fileheader_t) * i, SEEK_SET);
           write(fd, &fhdr, sizeof(fileheader_t));
       }
       close(fd);
    }
}

/* ----------------------------------------------------- */
/* 發表、回應、編輯、轉錄文章                            */
/* ----------------------------------------------------- */
static int 
solveEdFlagByBoard(const char *bn, int flags)
{
    if (
#ifdef BN_BBSMOVIE
        strcmp(bn, BN_BBSMOVIE) == 0 ||
#endif
#ifdef BN_TEST
        strcmp(bn, BN_TEST) == 0 ||
#endif
        0
    )
    {
    flags |= EDITFLAG_UPLOAD | EDITFLAG_ALLOWLARGE;
    }
    return flags;
}

void
do_reply_title(int row, const char *title, char result[STRLEN])
{
    char            genbuf[200];
    char            genbuf2[4];

    if (strncasecmp(title, str_reply, 4))
    snprintf(result, STRLEN, "Re: %s", title);
    else
    strlcpy(result, title, STRLEN);
    result[TTLEN - 1] = '\0';
    snprintf(genbuf, sizeof(genbuf), "採用原標題《%.60s》嗎?[Y] ", result);
    getdata(row, 0, genbuf, genbuf2, 4, LCECHO);
    if (genbuf2[0] == 'n')
    getdata(++row, 0, "標題:", result, TTLEN, DOECHO);
}

void 
do_crosspost(const char *brd, fileheader_t *postfile, const char *fpath,
             int isstamp)
{
    char            genbuf[200];
    int             len = 42-strlen(currboard);
    fileheader_t    fh;
    int bid = getbnum(brd);

    if(bid <= 0 || bid > MAX_BOARD) return;

    if(!strncasecmp(postfile->title, str_reply, 3))
        len=len+4;

    memcpy(&fh, postfile, sizeof(fileheader_t));
    if(isstamp) 
    {
         setbpath(genbuf, brd);
         stampfile(genbuf, &fh); 
    }
    else
         setbfile(genbuf, brd, postfile->filename);

    if(!strcasecmp(brd, BN_UNANONYMOUS))
       strcpy(fh.owner, cuser.userid);

    sprintf(fh.title,"%-*.*s.%s板",  len, len, postfile->title, currboard);
    unlink(genbuf);
    Copy((char *)fpath, genbuf);
    postfile->filemode = FILE_LOCAL;
    setbdir(genbuf, brd);
    if (append_record(genbuf, &fh, sizeof(fileheader_t)) != -1) {
    SHM->lastposttime[bid - 1] = now;
    touchbpostnum(bid, 1);
    }
}

static int
do_general(int garbage)
{
    fileheader_t    postfile;
    char            fpath[PATHLEN], buf[STRLEN];
    int i, j;
    int             defanony, ifuseanony;
    int             money = 0;
    char            genbuf[PATHLEN];
    const char      *owner;
    char            ctype[8][5] = {"問題", "建議", "討論", "心得",
                   "閒聊", "請益", "公告", "情報"};
    boardheader_t  *bp;
    int             islocal, posttype=-1, edflags = 0;
    char save_title[STRLEN];

    save_title[0] = '\0';

    ifuseanony = 0;
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    bp = getbcache(currbid);

    if( !CheckPostPerm()
#ifdef FOREIGN_REG
    // 不是外籍使用者在 PttForeign 板
    && !((cuser.uflag2 & FOREIGN) && 
        strcmp(bp->brdname, BN_FOREIGN) == 0)
#endif
    ) {
    vmsg("對不起,您目前無法在此發表文章!");
    return READ_REDRAW;
    }

#ifndef DEBUG
    if ( !CheckPostRestriction(currbid) )
    {
    vmsg("你不夠資深喔! (可按 i 查看限制)");
    return FULLUPDATE;
    }
#ifdef USE_COOLDOWN
   if(check_cooldown(bp))
       return READ_REDRAW;
#endif
#endif
    clear();

    setbfile(genbuf, currboard, FN_POST_NOTE);

    if (more(genbuf, NA) == -1) {
    more("etc/" FN_POST_NOTE, NA);
    }
    move(19, 0);
    prints("%s於【" ANSI_COLOR(33) " %s" ANSI_RESET " 】 "
       ANSI_COLOR(32) "%s" ANSI_RESET " 看板\n",
       "發表文章",
       currboard, bp->title + 7);

    if (quote_file[0])
    do_reply_title(20, currtitle, save_title);
    else {
    char tmp_title[STRLEN]="";
    move(21,0);
    outs("種類:");
    for(i=0; i<8 && bp->posttype[i*4]; i++)
        strlcpy(ctype[i],bp->posttype+4*i,5);
    if(i==0) i=8;
    for(j=0; j<i; j++)
        prints("%d.%4.4s ", j+1, ctype[j]);
    sprintf(buf,"(1-%d或不選)",i);
    getdata(21, 6+7*i, buf, tmp_title, 3, LCECHO); 
    posttype = tmp_title[0] - '1';
    if (posttype >= 0 && posttype < i)
        snprintf(tmp_title, sizeof(tmp_title),
            "[%s] ", ctype[posttype]);
    else
    {
        tmp_title[0] = '\0';
        posttype=-1;
    }
    getdata_buf(22, 0, "標題:", tmp_title, TTLEN, DOECHO);
    strip_ansi(tmp_title, tmp_title, STRIP_ALL);
    strlcpy(save_title, tmp_title, sizeof(save_title));
    }
    if (save_title[0] == '\0')
    return FULLUPDATE;

    curredit &= ~EDIT_MAIL;
    setutmpmode(POSTING);
    /* 未具備 Internet 權限者,只能在站內發表文章 */
    /* 板主預設站內存檔 */
    if (HasUserPerm(PERM_INTERNET) && !(bp->brdattr & BRD_LOCALSAVE))
    local_article = 0;
    else
    local_article = 1;

    /* build filename */
    setbpath(fpath, currboard);
    stampfile(fpath, &postfile);
    if(posttype!=-1 && ((1<<posttype) & bp->posttype_f)) {
    setbnfile(genbuf, bp->brdname, "postsample", posttype);
    Copy(genbuf, fpath);
    }

    edflags = EDITFLAG_ALLOWTITLE;
    edflags = solveEdFlagByBoard(currboard, edflags);

#if defined(PLAY_ANGEL) && defined(BN_ANGELPRAY)
    // XXX 惡搞的 code。
    if (HasUserPerm(PERM_ANGEL) && strcmp(currboard, BN_ANGELPRAY) == 0)
    {
    currbrdattr |= BRD_ANONYMOUS;
    currbrdattr |= BRD_DEFAULTANONYMOUS;
    };
#endif
    
    money = vedit2(fpath, YEA, &islocal, save_title, edflags);
    if (money == EDIT_ABORTED) {
    unlink(fpath);
    pressanykey();
    return FULLUPDATE;
    }
    /* set owner to Anonymous for Anonymous board */

#ifdef HAVE_ANONYMOUS
    /* Ptt and Jaky */
    defanony = currbrdattr & BRD_DEFAULTANONYMOUS;
    if ((currbrdattr & BRD_ANONYMOUS) &&
    ((strcmp(real_name, "r") && defanony) || (real_name[0] && !defanony))
    ) {
    strcat(real_name, ".");
    owner = real_name;
    ifuseanony = 1;
    } else
    owner = cuser.userid;
#else
    owner = cuser.userid;
#endif

    // ---- BEGIN OF MONEY VERIFICATION ----

    // money verification
#ifdef MAX_POST_MONEY
    if (money > MAX_POST_MONEY)
    money = MAX_POST_MONEY;
#endif

    // drop money & numposts for free boards
    // including: special boards (e.g. TEST, ALLPOST), bad boards, no BM boards
    if (IsFreeBoardName(currboard) || (currbrdattr&BRD_BAD) || bp->BM[0] < ' ') 
    {
    money = 0;
    }

    // also drop for anonymos/bid posts
    if(ifuseanony) {
    money = 0;
    postfile.filemode |= FILE_ANONYMOUS;
    postfile.multi.anon_uid = currutmp->uid;
    }
    else
    {
    /* general article */
    postfile.modified = dasht(fpath);
    postfile.multi.money = money;
    }

    // ---- END OF MONEY VERIFICATION ----

    strlcpy(postfile.owner, owner, sizeof(postfile.owner));
    strlcpy(postfile.title, save_title, sizeof(postfile.title));
    if (islocal)        /* local save */
    postfile.filemode |= FILE_LOCAL;

    setbdir(buf, currboard);

    // Ptt: stamp file again to make it order
    //      fix the bug that search failure in getindex
    //      stampfile_u is used when you don't want to clear other fields
    strcpy(genbuf, fpath);
    setbpath(fpath, currboard);
    stampfile_u(fpath, &postfile);   

    if (append_record(buf, &postfile, sizeof(postfile)) == -1)
    {
        unlink(genbuf);
    }
    else
    {
    char addPost = 0;
        rename(genbuf, fpath);
#ifdef LOGPOST
    {
            FILE    *fp = fopen("log/post", "a");
            fprintf(fp, "%d %s boards/%c/%s/%s\n",
                    now, cuser.userid, currboard[0], currboard,
                    postfile.filename);
            fclose(fp);
        }
#endif
    setbtotal(currbid);

    if( currmode & MODE_SELECT )
        append_record(currdirect, &postfile, sizeof(postfile));
    if( !islocal && !(bp->brdattr & BRD_NOTRAN) ){
        if( ifuseanony )
        outgo_post(&postfile, currboard, owner, "Anonymous.");
        else
        outgo_post(&postfile, currboard, cuser.userid, cuser.nickname);
    }
    brc_addlist(postfile.filename, postfile.modified);

        if( !bp->level || (currbrdattr & BRD_POSTMASK))
        {
            if ((now - cuser.firstlogin) / DAY_SECONDS < 14)
                    do_crosspost("NEWIDPOST", &postfile, fpath, 0);

        if (!(currbrdattr & BRD_HIDE) )
                    do_crosspost(BN_ALLPOST, &postfile, fpath, 0);
            else    
                    do_crosspost(BN_ALLHIDPOST, &postfile, fpath, 0);
    }
    outs("順利貼出佈告,");

    // Freeboard/BRD_BAD check was already done.
    if (!ifuseanony) 
    {
            if (money > 0)
        {
        demoney(money);    
        pwcuIncNumPost();
        addPost = 1;
        prints("這是您的第 %d 篇有效文章,獲得稿酬 %d 元",
            cuser.numposts, money);
        } else {
        // no money, no record.
        outs("本篇不列入記錄,敬請包涵。");
        }
    } 
    else 
    {
        outs("不列入記錄,敬請包涵。");
    }

    /* 回應到原作者信箱 */

    if (curredit & EDIT_BOTH) {
        char *str, *msg = "回應至作者信箱";

        genbuf[0] = 0;
        // XXX quote_user may contain invalid user, like '-' (deleted).
        if (is_validuserid(quote_user))
        {
        sethomepath(genbuf, quote_user);
        if (!dashd(genbuf))
        {
            genbuf[0] = 0;
            msg = err_uid;
        }
        }

        // now, genbuf[0] = "if user exists".
        if (genbuf[0])
        {
        stampfile(genbuf, &postfile);
        unlink(genbuf);
        Copy(fpath, genbuf);

        strlcpy(postfile.owner, cuser.userid, sizeof(postfile.owner));
        strlcpy(postfile.title, save_title, sizeof(postfile.title));
        sethomedir(genbuf, quote_user);
        if (append_record(genbuf, &postfile, sizeof(postfile)) == -1)
            msg = err_uid;
        else
            sendalert(quote_user, ALERT_NEW_MAIL);
        } else if ((str = strchr(quote_user, '.'))) {
        if ( bsmtp(fpath, save_title, str + 1, NULL) < 0)
            msg = "作者無法收信";
        } else {
        // unknown user id
        msg = "作者無法收信";
        }
        outs(msg);
        curredit ^= EDIT_BOTH;
    } // if (curredit & EDIT_BOTH)
    if (currbrdattr & BRD_ANONYMOUS)
            do_crosspost(BN_UNANONYMOUS, &postfile, fpath, 0);
#ifdef USE_COOLDOWN
        if(bp->nuser>30)
    {
        if (cooldowntimeof(usernum)<now)
        add_cooldowntime(usernum, 5);
    }
    add_posttimes(usernum, 1);
#endif
    // Notify all logins
    if (addPost)
    {

    }
    }
    pressanykey();
    return FULLUPDATE;
}

int
do_post(void)
{
    boardheader_t  *bp;
    STATINC(STAT_DOPOST);
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    bp = getbcache(currbid);
    if (bp->brdattr & BRD_VOTEBOARD)
    return do_voteboard(0);
    else if (!(bp->brdattr & BRD_GROUPBOARD))
    return do_general(0);
    return 0;
}

int
do_post_vote(void)
{
    return do_voteboard(1);
}

static void
do_generalboardreply(/*const*/ fileheader_t * fhdr)
{
    char            genbuf[3];
    
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);

    if (!CheckPostRestriction(currbid))
    {
    getdata(b_lines - 1, 0, 
#ifdef USE_PFTERM
        ANSI_COLOR(1;31) "▲ 無法回應至看板。 " ANSI_RESET
#else
        "▲ 無法回應至看板。 "
#endif
        "改回應至 (M)作者信箱 (Q)取消?[Q] ",
        genbuf, sizeof(genbuf), LCECHO);
    switch (genbuf[0]) {
        case 'm':
        mail_reply(0, fhdr, 0);
        break;
        default:
        break;
    }
    }
    else {
    getdata(b_lines - 1, 0,
        "▲ 回應至 (F)看板 (M)作者信箱 (B)二者皆是 (Q)取消?[F] ",
        genbuf, sizeof(genbuf), LCECHO);
    switch (genbuf[0]) {
        case 'm':
        mail_reply(0, fhdr, 0);
        case 'q':
        break;

        case 'b':
        curredit = EDIT_BOTH;
        default:
        strlcpy(currtitle, fhdr->title, sizeof(currtitle));
        strlcpy(quote_user, fhdr->owner, sizeof(quote_user));
        do_post();
        curredit &= ~EDIT_BOTH;
    }
    }
    *quote_file = 0;
}

int
b_call_in(int ent, const fileheader_t * fhdr, const char *direct)
{
    userinfo_t     *u = search_ulist(searchuser(fhdr->owner, NULL));
    if (u) {
    int             fri_stat;
    fri_stat = friend_stat(currutmp, u);
    if (isvisible_stat(currutmp, u, fri_stat) && call_in(u, fri_stat))
        return FULLUPDATE;
    }
    return DONOTHING;
}


static int
do_reply(/*const*/ fileheader_t * fhdr)
{
    boardheader_t  *bp;
    if (!fhdr || !fhdr->filename[0] || fhdr->owner[0] == '-')
    return DONOTHING;

    if (!CheckPostPerm() ) return DONOTHING;
    if (fhdr->filemode &FILE_SOLVED)
     {
       if(fhdr->filemode & FILE_MARKED)
         {
          vmsg("很抱歉, 此文章已結案並標記, 不得回應.");
          return FULLUPDATE;
         }
       if(vmsg("此篇文章已結案, 是否真的要回應?(y/N)")!='y')
          return FULLUPDATE;
     }

    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    bp = getbcache(currbid);
    if (bp->brdattr & BRD_NOREPLY) {
    // try to reply by mail.
    if (vans("很抱歉, 本板不開放回覆文章,要改回信給作者嗎? [y/N]: ") == 'y')
        return mail_reply(0, fhdr, 0);
    else
        return FULLUPDATE;
    }

    setbfile(quote_file, bp->brdname, fhdr->filename);
    if (bp->brdattr & BRD_VOTEBOARD || (fhdr->filemode & FILE_VOTE))
    do_voteboardreply(fhdr);
    else
    do_generalboardreply(fhdr);
    *quote_file = 0;
    return FULLUPDATE;
}

static int
reply_post(int ent, /*const*/ fileheader_t * fhdr, const char *direct)
{
    return do_reply(fhdr);
}

#ifdef EDITPOST_SMARTMERGE

#define HASHPF_RET_OK (0)

// return: 0 - ok; otherwise - fail.
static int
hash_partial_file( char *path, size_t sz, unsigned char output[SMHASHLEN] )
{
    int fd;
    size_t n;
    unsigned char buf[1024];

    Fnv64_t fnvseed = FNV1_64_INIT;
    assert(SMHASHLEN == sizeof(fnvseed));

    fd = open(path, O_RDONLY);
    if (fd < 0)
    return 1;

    while(  sz > 0 &&
        (n = read(fd, buf, sizeof(buf))) > 0 )
    {
    if (n > sz) n = sz;
    fnvseed = fnv_64_buf(buf, (int) n, fnvseed);
    sz -= n;
    }
    close(fd);

    if (sz > 0) // file is different
    return 2;

    memcpy(output, (void*) &fnvseed, sizeof(fnvseed));
    return HASHPF_RET_OK;
}
#endif // EDITPOST_SMARTMERGE

int
edit_post(int ent, fileheader_t * fhdr, const char *direct)
{
    char            fpath[80];
    char            genbuf[200];
    fileheader_t    postfile;
    boardheader_t  *bp = getbcache(currbid);
    // int          recordTouched = 0;
    time4_t     oldmt, newmt;
    off_t       oldsz;
    int         edflags = 0;
    char save_title[STRLEN];
    save_title[0] = '\0';

#ifdef EDITPOST_SMARTMERGE
    char        canDoSmartMerge = 1;
#endif // EDITPOST_SMARTMERGE

#ifdef EXP_EDITPOST_TEXTONLY
    // experimental: "text only" editing
    edflags |= EXP_EDITPOST_TEXTONLY;
#endif

    assert(0<=currbid-1 && currbid-1<MAX_BOARD && bp);

    // special modes (plus MODE_DIGEST?)
    if( currmode & MODE_SELECT )
    return DONOTHING;

    // board check
    if (is_readonly_board(bp->brdname) ||
    (bp->brdattr & BRD_VOTEBOARD))
    return DONOTHING;

    // file check
    if (fhdr->filemode & FILE_VOTE)
    return DONOTHING;

#ifdef SAFE_ARTICLE_DELETE
    if( fhdr->filename[0] == '.' )
    return DONOTHING;
#endif

    // user check
    if (!HasUserPerm(PERM_BASIC) || // includeing guests
    !CheckPostPerm() )   
    return DONOTHING;

    if (strcmp(fhdr->owner, cuser.userid) != EQUSTR)
    {
    if (!HasUserPerm(PERM_SYSOP))
        return DONOTHING;

    // admin edit!
    log_filef("log/security", LOG_CREAT,
        "%d %s %d %s admin edit (board) file=%s\n", 
        (int)now, Cdate(&now), getpid(), cuser.userid, fpath);
    }

    edflags = EDITFLAG_ALLOWTITLE;
    edflags = solveEdFlagByBoard(bp->brdname, edflags);

    setutmpmode(REEDIT);


    // XXX 不知何時起, edit_post 已經不會有 + 號了...
    // 全部都是 Sysop Edit 的原地形式。
    // 哪天有空找個人寫個 mode 是改名 edit 吧
    //
    // TODO 由於現在檔案都是直接蓋回原檔,
    // 在原看板目錄開已沒有很大意義。 (效率稍高一點)
    // 可以考慮改開在 user home dir
    // 好處是看板的檔案數不會狂成長。 (when someone crashed)
    // sethomedir(fpath, cuser.userid);
    // XXX 如果你的系統有定期看板清孤兒檔,那就不用放 user home。
    setbpath(fpath, currboard);

    // XXX 以現在的模式,這是個 temp file
    stampfile(fpath, &postfile);
    setdirpath(genbuf, direct, fhdr->filename);
    local_article = fhdr->filemode & FILE_LOCAL;

    // copying takes long time, add some visual effect
    grayout(0, b_lines-2, GRAYOUT_DARK);
    move(b_lines-1, 0); clrtoeol();
    outs("正在載入檔案...");
    refresh();

    Copy(genbuf, fpath);
    strlcpy(save_title, fhdr->title, sizeof(save_title));

    // so far this is what we copied now...
    oldmt = dasht(genbuf);
    oldsz = dashs(fpath); // should be equal to genbuf(src).
              // use fpath (dest) in case some 
              // modification was made.
    do {
#ifdef EDITPOST_SMARTMERGE

    unsigned char oldsum[SMHASHLEN] = {0}, newsum[SMHASHLEN] = {0};

    //  make checksum of file genbuf
    if (canDoSmartMerge &&
        hash_partial_file(fpath, oldsz, oldsum) != HASHPF_RET_OK)
        canDoSmartMerge = 0;

#endif // EDITPOST_SMARTMERGE


    if (vedit2(fpath, 0, NULL, save_title, edflags) == EDIT_ABORTED)
        break;

    newmt = dasht(genbuf);

#ifdef EDITPOST_SMARTMERGE

    // only merge if file is enlarged and modified
    if (newmt == oldmt || dashs(genbuf) < oldsz)
        canDoSmartMerge = 0;
    
    // make checksum of new file [by oldsz]
    if (canDoSmartMerge &&
        hash_partial_file(genbuf, oldsz, newsum) != HASHPF_RET_OK)
        canDoSmartMerge = 0;

    // verify checksum
    if (canDoSmartMerge &&
        memcmp(oldsum, newsum, sizeof(newsum)) != 0)
        canDoSmartMerge = 0;

    if (canDoSmartMerge)
    {
        canDoSmartMerge = 0; // only try merge once

        move(b_lines-7, 0);
        clrtobot();
        outs(ANSI_COLOR(1;33) "▲ 檔案已被修改過! ▲" ANSI_RESET "\n\n");
        outs("進行自動合併 [Smart Merge]...\n");

        // smart merge
        if (AppendTail(genbuf, fpath, oldsz) == 0)
        {
        // merge ok
        oldmt = newmt;
        outs(ANSI_COLOR(1) 
            "合併成功\,新修改(或推文)已加入您的文章中。\n" 
            "您沒有蓋\掉任何推文或修改,請勿擔心。"
            ANSI_RESET "\n");

#ifdef  WARN_EXP_SMARTMERGE
        outs(ANSI_COLOR(1;33) 
            "自動合併 (Smart Merge) 是實驗中的新功\能," 
            "請檢查一下您的文章合併後是否正常。" ANSI_RESET "\n"
            "若有問題請至 " BN_BUGREPORT " 板報告,謝謝。");
#endif 
        vmsg("合併完成");
        } else {
        outs(ANSI_COLOR(31) 
            "自動合併失敗。 請改用人工手動編輯合併。" ANSI_RESET);
        vmsg("合併失敗");
        }
    }

#endif // EDITPOST_SMARTMERGE

    if (oldmt != newmt)
    {
        int c = 0;

        move(b_lines-7, 0);
        clrtobot();
        outs(ANSI_COLOR(1;31) "▲ 檔案已被修改過! ▲" ANSI_RESET "\n\n");

        outs("可能是您在編輯的過程中有人進行推文或修文。\n"
         "您可以選擇直接覆蓋\檔案(y)、放棄(n),\n"
         " 或是" ANSI_COLOR(1)"重新編輯" ANSI_RESET
         "(新文會被貼到剛編的檔案後面)(e)。\n");
        c = tolower(vans("要直接覆蓋\檔案/取消/重編嗎 [Y/n/e]?"));

        if (c == 'n')
        break;

        if (c == 'e')
        {
        FILE *fp, *src;

        /* merge new and old stuff */
        fp = fopen(fpath, "at"); 
        src = fopen(genbuf, "rt");

        if(!fp)
        {
            vmsg("抱歉,檔案已損毀。");
            if(src) fclose(src);
            unlink(fpath); // fpath is a temp file
            return FULLUPDATE;
        }

        if(src)
        {
            int c = 0;

            fprintf(fp, MSG_SEPERATOR "\n");
            fprintf(fp, "以下為被修改過的最新內容: ");
            fprintf(fp,
                " (%s)\n",
                Cdate_mdHM(&newmt));
            fprintf(fp, MSG_SEPERATOR "\n");
            while ((c = fgetc(src)) >= 0)
            fputc(c, fp);
            fclose(src);

            // update oldsz, old mt records
            oldmt = dasht(genbuf);
            oldsz = dashs(genbuf);
        }
        fclose(fp);
        continue;
        }
    }

    // OK to save file.

    // piaip Wed Jan  9 11:11:33 CST 2008
    // in order to prevent calling system 'mv' all the
    // time, it is better to unlink() first, which
    // increased the chance of succesfully using rename().
    // WARNING: if genbuf and fpath are in different directory,
    // you should disable pre-unlinking
    unlink(genbuf);
        Rename(fpath, genbuf);

    fhdr->modified = dasht(genbuf);
    strlcpy(fhdr->title, save_title, sizeof(fhdr->title));

    if (fhdr->modified > 0)
    {
        // substitute_ref_record(direct, fhdr, ent);
        modify_dir_lite(direct, ent, fhdr->filename,
            fhdr->modified, save_title, 0);

        // mark my self as "read this file".
        brc_addlist(fhdr->filename, fhdr->modified);
    }
    break;

    } while (1);

    /* should we do this when editing was aborted? */
    unlink(fpath);

    return FULLUPDATE;
}

#define UPDATE_USEREC   (currmode |= MODE_DIRTY)

static int
cp_IsHiddenBoard(const boardheader_t *bp)
{
    // rules: see HasBoardPerm().
    if ((bp->brdattr & BRD_HIDE) && (bp->brdattr & BRD_POSTMASK)) 
    return 1;
    if (bp->level && !(bp->brdattr & BRD_POSTMASK))
    return 1;
    return 0;
}

static int
cross_post(int ent, fileheader_t * fhdr, const char *direct)
{
    char            xboard[20], fname[PATHLEN], xfpath[PATHLEN], xtitle[80];
    char            inputbuf[10], genbuf[200], genbuf2[4];
    fileheader_t    xfile;
    FILE           *xptr;
    int             author, xbid, hashPost;
    boardheader_t  *bp;

    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    bp = getbcache(currbid);

    if (bp && (bp->brdattr & BRD_VOTEBOARD) )
    return DONOTHING;

    // if file is SAFE_DELETED, skip it.
    if (fhdr->owner[0] == '-' && fhdr->owner[1] == 0)
    return DONOTHING;

    setbfile(fname, currboard, fhdr->filename);
    if (!dashf(fname))
    {
    vmsg("檔案已不存在。");
    return FULLUPDATE;
    }

#ifdef USE_AUTOCPLOG
    // anti-crosspost spammers
    //
    // some spammers try to cross-post to other boards without
    // restriction (see pakkei0712* events on 2007/12)
    // for (1) increase numpost (2) flood target board 
    // (3) flood original post
    // You must have post permission on current board
    //
    if( (bp->brdattr & BRD_CPLOG) && 
    (!CheckPostPerm() || !CheckPostRestriction(currbid)))
    {
    vmsg("由本板轉錄文章需有發文權限(可按 i 查看限制)");
    return FULLUPDATE;
    }
#endif // USE_AUTOCPLOG

    // XXX TODO 為避免違法使用者大量對申訴板轉文,限定每次發文量。
    if (HasUserPerm(PERM_VIOLATELAW))
    {
    static int violatecp = 0;
    if (violatecp++ >= MAX_CROSSNUM)
        return DONOTHING;
    }

    move(2, 0);
    clrtoeol();
    if (postrecord.times > 1)
    {
    outs(ANSI_COLOR(1;31) 
    "請注意: 若過量重複轉錄將視為洗板,導致被開罰單停權。\n" ANSI_RESET
    "若有特別需求請洽各板主,請他們幫你轉文。\n\n");
    }
    move(1, 0);

    CompleteBoard("轉錄本文章於看板:", xboard);
    if (*xboard == '\0')
    return FULLUPDATE;

    if (!haspostperm(xboard))
    {
    vmsg("看板不存在或該看板禁止您發表文章!");
    return FULLUPDATE;
    }

    /* 不要借用變數,記憶體沒那麼缺,人腦混亂的代價比較高 */

    // XXX cross-posting a series of articles should not be cross-post?
    // so let's use filename instead of title.
    // hashPost = StringHash(fhdr->title); // why use title?
    hashPost = StringHash(fhdr->filename); // let's try filename
    xbid = getbnum(xboard);
    assert(0<=xbid-1 && xbid-1<MAX_BOARD);

    if (xbid == currbid)
    {
    vmsg("同板不需轉錄。");
    return FULLUPDATE;
    }

    // check target postperm
    if (!CheckPostRestriction(xbid))
    {
    vmsg("你不夠資深喔! (可在目的看板內按 i 查看限制)");
    return FULLUPDATE;
    }

    // quick check: if already cross-posted, reject.
    if (hashPost == postrecord.checksum[0])
    {
    if (xbid == postrecord.last_bid) 
    {
        vmsg("這篇文章已經轉錄過了。");
        return FULLUPDATE;
    } 
    else if (postrecord.times >= MAX_CROSSNUM)
    {
        anticrosspost();
        return FULLUPDATE;
    }
    }

#ifdef USE_COOLDOWN
    if(check_cooldown(getbcache(xbid)))
    {
    vmsg("該看板現在無法轉錄。");
    return FULLUPDATE;
    }
#endif

    ent = 1;
    author = 0;
    if (HasUserPerm(PERM_SYSOP) || !strcmp(fhdr->owner, cuser.userid)) {
    getdata(2, 0, "(1)原文轉載 (2)舊轉錄格式?[1] ",
        genbuf, 3, DOECHO);
    if (genbuf[0] != '2') {
        ent = 0;
        getdata(2, 0, "保留原作者名稱嗎?[Y] ", inputbuf, 3, DOECHO);
        if (inputbuf[0] != 'n' && inputbuf[0] != 'N')
        author = '1';
    }
    }
    if (ent)
    snprintf(xtitle, sizeof(xtitle), "[轉錄]%.66s", fhdr->title);
    else
    strlcpy(xtitle, fhdr->title, sizeof(xtitle));

    snprintf(genbuf, sizeof(genbuf), "採用原標題《%.60s》嗎?[Y] ", xtitle);
    getdata(2, 0, genbuf, genbuf2, 4, LCECHO);
    if (genbuf2[0] == 'n') {
    if (getdata_str(2, 0, "標題:", genbuf, TTLEN, DOECHO, xtitle))
        strlcpy(xtitle, genbuf, sizeof(xtitle));
    }

    getdata(2, 0, "(S)存檔 (L)站內 (Q)取消?[Q] ", genbuf, 3, LCECHO);

    if (genbuf[0] == 'l' || genbuf[0] == 's') {
    int             currmode0 = currmode;
    const char     *save_currboard;

    currmode = 0;
    setbpath(xfpath, xboard);
    stampfile(xfpath, &xfile);
    if (author)
        strlcpy(xfile.owner, fhdr->owner, sizeof(xfile.owner));
    else
        strlcpy(xfile.owner, cuser.userid, sizeof(xfile.owner));
    strlcpy(xfile.title, xtitle, sizeof(xfile.title));
    if (genbuf[0] == 'l') {
        xfile.filemode = FILE_LOCAL;
    }
    setbfile(fname, currboard, fhdr->filename);
    xptr = fopen(xfpath, "w");

    save_currboard = currboard;
    currboard = xboard;
    write_header(xptr, xfile.title);
    currboard = save_currboard;

    if (cp_IsHiddenBoard(bp))
    {
        /* invisible board */
        fprintf(xptr, "※ [本文轉錄自某隱形看板]\n\n");
        b_suckinfile_invis(xptr, fname, currboard);
    } else {
        /* public board */
        fprintf(xptr, "※ [本文轉錄自 %s 看板]\n\n", currboard);
        b_suckinfile(xptr, fname);
    }

    addsignature(xptr, 0);
    fclose(xptr);

#ifdef USE_AUTOCPLOG
    /* add cp log. bp is currboard now. */
    if(bp->brdattr & BRD_CPLOG)
    {
        char buf[PATHLEN], tail[STRLEN];
        char bname[STRLEN] = "";
        int maxlength = 51 +2 - 6;
        int bid = getbnum(xboard);

        assert(0<=bid-1 && bid-1<MAX_BOARD);
        bp = getbcache(bid);
        if (cp_IsHiddenBoard(bp))
        {
        strcpy(bname, "某隱形看板");
        } else {
        sprintf(bname, "看板 %s", xboard);
        }

        maxlength -= (strlen(cuser.userid) + strlen(bname));

#ifdef GUESTRECOMMEND
        snprintf(tail, sizeof(tail),
            "%15s %s",
            FROMHOST, Cdate_md(&now));
#else
        maxlength += (15 - 6);
        snprintf(tail, sizeof(tail),
            " %s",
            Cdate_mdHM(&now));
#endif
        snprintf(buf, sizeof(buf),
            // ANSI_COLOR(32) <- system will add green
            "※ " ANSI_COLOR(1;32) "%s"
            ANSI_COLOR(0;32) ":轉錄至"
            "%s" ANSI_RESET "%*s%s\n" ,
            cuser.userid, bname, maxlength, "",
            tail);

        do_add_recommend(direct, fhdr,  ent, buf, 2);
    } else
#endif
    {
        /* now point bp to new bord */
        xbid = getbnum(xboard);
        assert(0<=xbid-1 && xbid-1<MAX_BOARD);
        bp = getbcache(xbid);
    }

    /*
     * Cross fs有問題 else { unlink(xfpath); link(fname, xfpath); }
     */
    setbdir(fname, xboard);
    append_record(fname, &xfile, sizeof(xfile));
    if (!xfile.filemode && !(bp->brdattr & BRD_NOTRAN))
        outgo_post(&xfile, xboard, cuser.userid, cuser.nickname);
#ifdef USE_COOLDOWN
        if(bp->nuser>30)
    {
        if (cooldowntimeof(usernum)<now)
        add_cooldowntime(usernum, 5);
    }
    add_posttimes(usernum, 1);
#endif
    setbtotal(getbnum(xboard));
    outs("文章轉錄完成。(轉錄不增加文章數,敬請包涵) ");

    // update crosspost record
    if (hashPost == postrecord.checksum[0]) 
        // && xbid != postrecord.last_bid)
    {
        ++postrecord.times; // check will be done next time.

        if (postrecord.times == MAX_CROSSNUM) 
        {
        outs(ANSI_COLOR(1;31) " 警告: 即將達到轉錄次數上限,"
            "下次將開罰單!" ANSI_RESET);
        }
    } else {
        // reset cross-post record
        postrecord.times = 0;
        postrecord.last_bid = xbid;
        postrecord.checksum[0] = hashPost;
    }

    pressanykey();
    currmode = currmode0;
    }

    return FULLUPDATE;
}

static int
read_post(int ent, fileheader_t * fhdr, const char *direct)
{
    char            genbuf[100];
    int             more_result;

    if (fhdr->owner[0] == '-' || fhdr->filename[0] == 'L' || !fhdr->filename[0])
    return READ_SKIP;

    STATINC(STAT_READPOST);
    setdirpath(genbuf, direct, fhdr->filename);

    more_result = more(genbuf, YEA);

#ifdef LOG_CRAWLER
    {
        // kcwu: log crawler
    static int read_count = 0;
        extern Fnv32_t client_code;

        ++read_count;
        if (read_count % 1000 == 0) {
            time4_t t = time4(NULL);
            log_filef("log/read_alot", LOG_CREAT,
            "%d %s %d %s %08x %d\n", t, Cdate(&t), getpid(),
            cuser.userid, client_code, read_count);
        }
    }
#endif // LOG_CRAWLER

    {
    int posttime=atoi(fhdr->filename+2);
    if(posttime>now-12*3600)
        STATINC(STAT_READPOST_12HR);
    else if(posttime>now-1*DAY_SECONDS)
        STATINC(STAT_READPOST_1DAY);
    else if(posttime>now-3*DAY_SECONDS)
        STATINC(STAT_READPOST_3DAY);
    else if(posttime>now-7*DAY_SECONDS)
        STATINC(STAT_READPOST_7DAY);
    else
        STATINC(STAT_READPOST_OLD);
    }
    brc_addlist(fhdr->filename, fhdr->modified);
    strlcpy(currtitle, subject(fhdr->title), sizeof(currtitle));

    switch(more_result)
    {
    case -1:
        clear();
        vmsg("此文章無內容");
        return FULLUPDATE;
    case RET_DOREPLY:
    case RET_DOREPLYALL:
        do_reply(fhdr);
            return FULLUPDATE;
    case RET_DORECOMMEND:
            recommend(ent, fhdr, direct);
        return FULLUPDATE;
    case RET_DOQUERYINFO:
        view_postinfo(ent, fhdr, direct, b_lines-3);
        return FULLUPDATE;
    }
    if(more_result)
    return more_result;
    return FULLUPDATE;
}

void
editLimits(unsigned char *pregtime, unsigned char *pposts, unsigned char *pbadpost)
{
    char genbuf[STRLEN];
    int  temp;

    // load var
    unsigned char 
    regtime = *pregtime, 
    posts   = *pposts, 
    badpost = *pbadpost;

    // query UI
    sprintf(genbuf, "%u", regtime);
    do {
    getdata_buf(b_lines - 1, 0, 
        "註冊時間限制 (以'月'為單位,0~255):", genbuf, 4, NUMECHO);
    temp = atoi(genbuf);
    } while (temp < 0 || temp > 255);
    regtime = (unsigned char)temp;

    sprintf(genbuf, "%u", posts*10);
    do {
    getdata_buf(b_lines - 1, 0, 
        "文章篇數下限 (0~2550,以10為單位,個位數字將自動捨去):", genbuf, 5, NUMECHO);
    temp = atoi(genbuf);
    } while (temp < 0 || temp > 2550);
    posts = (unsigned char)(temp / 10);

    sprintf(genbuf, "%u", 255 - badpost);
    do {
    getdata_buf(b_lines - 1, 0, 
        "劣文篇數上限 (0~255):", genbuf, 5, NUMECHO);
    temp = atoi(genbuf);
    } while (temp < 0 || temp > 255);
    badpost = (unsigned char)(255 - temp);

    // save var
    *pregtime = regtime;
    *pposts   = posts;
    *pbadpost = badpost;
}

int
do_limitedit(int ent, fileheader_t * fhdr, const char *direct)
{
    char buf[STRLEN];
    boardheader_t  *bp = getbcache(currbid);

    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    if (!((currmode & MODE_BOARD) || HasUserPerm(PERM_SYSOP) ||
        (HasUserPerm(PERM_SYSSUPERSUBOP) && GROUPOP())))
    return DONOTHING;
    
    strcpy(buf, "更改 ");
    if (HasUserPerm(PERM_SYSOP) || (HasUserPerm(PERM_SYSSUPERSUBOP) && GROUPOP()))
    strcat(buf, "(A)本板發表限制 ");
    strcat(buf, "(B)本板預設");
    if (fhdr->filemode & FILE_VOTE)
    strcat(buf, " (C)本篇");
    strcat(buf, "連署限制 (Q)取消?[Q]");
    buf[0] = vans(buf);

    if ((HasUserPerm(PERM_SYSOP) || (HasUserPerm(PERM_SYSSUPERSUBOP) && GROUPOP())) && buf[0] == 'a') {

    editLimits(
        &bp->post_limit_regtime,
        &bp->post_limit_posts,
        &bp->post_limit_badpost);

    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    substitute_record(fn_board, bp, sizeof(boardheader_t), currbid);
    log_usies("SetBoard", bp->brdname);
    vmsg("修改完成!");
    return FULLUPDATE;
    }
    else if (buf[0] == 'b') {

    editLimits(
        &bp->vote_limit_regtime,
        &bp->vote_limit_posts,
        &bp->vote_limit_badpost);

    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    substitute_record(fn_board, bp, sizeof(boardheader_t), currbid);
    log_usies("SetBoard", bp->brdname);
    vmsg("修改完成!");
    return FULLUPDATE;
    }
    else if ((fhdr->filemode & FILE_VOTE) && buf[0] == 'c') {

    editLimits(
        &fhdr->multi.vote_limits.regtime,
        &fhdr->multi.vote_limits.posts,
        &fhdr->multi.vote_limits.badpost);

    substitute_ref_record(direct, fhdr, ent);
    vmsg("修改完成!");
    return FULLUPDATE;
    }
    vmsg("取消修改");
    return FULLUPDATE;
}

/* ----------------------------------------------------- */
/* 採集精華區                                            */
/* ----------------------------------------------------- */
static int
b_man(void)
{
    char            buf[PATHLEN];

    setapath(buf, currboard);
    if ((currmode & MODE_BOARD) || HasUserPerm(PERM_SYSOP)) {
    char            genbuf[128];
    int             fd;
    snprintf(genbuf, sizeof(genbuf), "%s/.rebuild", buf);
    if ((fd = open(genbuf, O_CREAT, 0640)) > 0)
        close(fd);
    }
    return a_menu(currboard, buf, HasUserPerm(PERM_ALLBOARD) ? 2 :
          (currmode & MODE_BOARD ? 1 : 0), 
          currbid, // getbnum(currboard)?
          NULL);
}

#ifndef NO_GAMBLE
static int
stop_gamble(void)
{
    boardheader_t  *bp = getbcache(currbid);
    char            fn_ticket[128], fn_ticket_end[128];
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    if (!bp->endgamble || bp->endgamble > now)
    return 0;

    setbfile(fn_ticket, currboard, FN_TICKET);
    setbfile(fn_ticket_end, currboard, FN_TICKET_END);

    rename(fn_ticket, fn_ticket_end);
    if (bp->endgamble) {
    bp->endgamble = 0;
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    substitute_record(fn_board, bp, sizeof(boardheader_t), currbid);
    }
    return 1;
}
static int
join_gamble(int ent, const fileheader_t * fhdr, const char *direct)
{
    if (!HasUserPerm(PERM_LOGINOK))
    return DONOTHING;
    if (stop_gamble()) {
    vmsg("目前未舉辦賭盤或賭盤已開獎");
    return DONOTHING;
    }
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    ticket(currbid);
    return FULLUPDATE;
}
static int
hold_gamble(void)
{
    char            fn_ticket[128], fn_ticket_end[128], genbuf[128], msg[256] = "",
                    yn[10] = "";
    char tmp[128];
    boardheader_t  *bp = getbcache(currbid);
    int             i;
    FILE           *fp = NULL;

    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    if (!(currmode & MODE_BOARD))
    return 0;
    if (bp->brdattr & BRD_BAD )
    {
          vmsg("違法看板禁止使用賭盤");
      return 0;
    }

    setbfile(fn_ticket, currboard, FN_TICKET);
    setbfile(fn_ticket_end, currboard, FN_TICKET_END);
    setbfile(genbuf, currboard, FN_TICKET_LOCK);

    if (dashf(fn_ticket)) {
    getdata(b_lines - 1, 0, "已經有舉辦賭盤, "
        "是否要 [停止下注]?(N/y):", yn, 3, LCECHO);
    if (yn[0] != 'y')
        return FULLUPDATE;
    rename(fn_ticket, fn_ticket_end);
    if (bp->endgamble) {
        bp->endgamble = 0;
        assert(0<=currbid-1 && currbid-1<MAX_BOARD);
        substitute_record(fn_board, bp, sizeof(boardheader_t), currbid);

    }
    return FULLUPDATE;
    }
    if (dashf(fn_ticket_end)) {
    getdata(b_lines - 1, 0, "已經有舉辦賭盤, "
        "是否要開獎 [否/是]?(N/y):", yn, 3, LCECHO);
    if (yn[0] != 'y')
        return FULLUPDATE;
        if(cpuload(NULL) > MAX_CPULOAD/4)
            {
            vmsg("負荷過高 請於系統負荷低時開獎..");
        return FULLUPDATE;
        }
    openticket(currbid);
    return FULLUPDATE;
    } else if (dashf(genbuf)) {
    vmsg(" 目前系統正在處理開獎事宜, 請結果出爐後再舉辦.......");
    return FULLUPDATE;
    }
    getdata(b_lines - 2, 0, "要舉辦賭盤 (N/y):", yn, 3, LCECHO);
    if (yn[0] != 'y')
    return FULLUPDATE;
    getdata(b_lines - 1, 0, "賭什麼? 請輸入主題 (輸入後編輯內容):",
        msg, 20, DOECHO);
    if (msg[0] == 0 ||
    veditfile(fn_ticket_end) < 0)
    return FULLUPDATE;

    clear();
    showtitle("舉辦賭盤", BBSNAME);
    setbfile(tmp, currboard, FN_TICKET_ITEMS ".tmp");

    //sprintf(genbuf, "%s/" FN_TICKET_ITEMS, direct);

    if (!(fp = fopen(tmp, "w")))
    return FULLUPDATE;
    do {
    getdata(2, 0, "輸入彩票價格 (價格:10-10000):", yn, 6, NUMECHO);
    i = atoi(yn);
    } while (i < 10 || i > 10000);
    fprintf(fp, "%d\n", i);
    if (!getdata(3, 0, "設定自動封盤時間?(Y/n)", yn, 3, LCECHO) || yn[0] != 'n') {
    bp->endgamble = gettime(4, now, "封盤於");
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    substitute_record(fn_board, bp, sizeof(boardheader_t), currbid);
    }
    move(6, 0);
    snprintf(genbuf, sizeof(genbuf),
         "\n請到 %s 板 按'f'參與賭博!\n\n"
         "一張 %d " MONEYNAME "幣, 這是%s的賭博\n%s%s\n",
         currboard,
         i, i < 100 ? "小賭式" : i < 500 ? "平民級" :
         i < 1000 ? "貴族級" : i < 5000 ? "富豪級" : "傾家蕩產",
         bp->endgamble ? "賭盤結束時間: " : "",
         bp->endgamble ? Cdate(&bp->endgamble) : ""
         );
    strcat(msg, genbuf);
    outs("請依次輸入彩票名稱, 需提供2~8項. (未滿八項, 輸入直接按Enter)\n");
    //outs(ANSI_COLOR(1;33) "注意輸入後無法修改!\n");
    for( i = 0 ; i < 8 ; ++i ){
    snprintf(yn, sizeof(yn), " %d)", i + 1);
    getdata(7 + i, 0, yn, genbuf, 9, DOECHO);
    if (!genbuf[0] && i > 1)
        break;
    fprintf(fp, "%s\n", genbuf);
    }
    fclose(fp);

    setbfile(genbuf, currboard, FN_TICKET_RECORD);
    unlink(genbuf); // Ptt: 防堵利用不同id同時舉辦賭場
    setbfile(genbuf, currboard, FN_TICKET_USER);
    unlink(genbuf); // Ptt: 防堵利用不同id同時舉辦賭場

    setbfile(genbuf, currboard, FN_TICKET_ITEMS);
    setbfile(tmp, currboard, FN_TICKET_ITEMS ".tmp");
    if(!dashf(fn_ticket))
    Rename(tmp, genbuf);

    snprintf(genbuf, sizeof(genbuf), "[公告] %s 板 開始賭博!", currboard);
    post_msg(currboard, genbuf, msg, cuser.userid);
    post_msg("Record", genbuf + 7, msg, "[馬路探子]");
    /* Tim 控制CS, 以免正在玩的user把資料已經寫進來 */
    rename(fn_ticket_end, fn_ticket);
    /* 設定完才把檔名改過來 */

    vmsg("賭盤設定完成");
    return FULLUPDATE;
}
#endif

static int
cite_post(int ent, const fileheader_t * fhdr, const char *direct)
{
    char            fpath[PATHLEN];
    char            title[TTLEN + 1];

    setbfile(fpath, currboard, fhdr->filename);
    strlcpy(title, "◇ ", sizeof(title));
    strlcpy(title + 3, fhdr->title, TTLEN - 3);
    title[TTLEN] = '\0';
    a_copyitem(fpath, title, 0, 1);
    b_man();
    return FULLUPDATE;
}

int
edit_title(int ent, fileheader_t * fhdr, const char *direct)
{
    char            genbuf[200] = "";
    fileheader_t    tmpfhdr = *fhdr;
    int             dirty = 0;
    int allow = 0;

    // should we allow edit-title here?
    if (currstat == RMAIL)
    allow = 0;
    else if (HasUserPerm(PERM_SYSOP))
    allow = 2;
    else if (currmode & MODE_BOARD ||
         strcmp(cuser.userid, fhdr->owner) == 0)
    allow = 1;

    if (!allow)
    return DONOTHING;

    if (fhdr && fhdr->title[0])
    strlcpy(genbuf, fhdr->title, TTLEN+1);

    if (getdata_buf(b_lines - 1, 0, "標題:", genbuf, TTLEN, DOECHO)) {
    strlcpy(tmpfhdr.title, genbuf, sizeof(tmpfhdr.title));
    dirty++;
    }

    if (allow >= 2) 
    {
    if (getdata(b_lines - 1, 0, "作者:", genbuf, IDLEN + 2, DOECHO)) {
        strlcpy(tmpfhdr.owner, genbuf, sizeof(tmpfhdr.owner));
        dirty++;
    }
    if (getdata(b_lines - 1, 0, "日期:", genbuf, 6, DOECHO)) {
        snprintf(tmpfhdr.date, sizeof(tmpfhdr.date), "%.5s", genbuf);
        dirty++;
    }
    }

    if (dirty)
    {
    getdata(b_lines - 1, 0, "確定(Y/N)?[n] ", genbuf, 3, DOECHO);
    if ((genbuf[0] == 'y' || genbuf[0] == 'Y') && dirty) {
        // TODO verify if record is still valid
        fileheader_t curr;
        memset(&curr, 0, sizeof(curr));
        if (get_record(direct, &curr, sizeof(curr), ent) < 0 ||
        strcmp(curr.filename, fhdr->filename) != 0)
        {
        // modified...
        vmsg("抱歉,系統忙碌中,請稍後再試。");
        return FULLUPDATE;
        }
        *fhdr = tmpfhdr;
        substitute_ref_record(direct, fhdr, ent);
    }
    }
    return FULLUPDATE;
}

static int
solve_post(int ent, fileheader_t * fhdr, const char *direct)
{
    if ((currmode & MODE_BOARD)) {
    fhdr->filemode ^= FILE_SOLVED;
        substitute_ref_record(direct, fhdr, ent);
    check_locked(fhdr);
    return PART_REDRAW;
    }
    return DONOTHING;
}


static int
recommend_cancel(int ent, fileheader_t * fhdr, const char *direct)
{
    char            yn[5];
    if (!(currmode & MODE_BOARD))
    return DONOTHING;
    getdata(b_lines - 1, 0, "確定要推薦歸零[y/N]? ", yn, 5, LCECHO);
    if (yn[0] != 'y')
    return FULLUPDATE;
    fhdr->recommend = 0;

    substitute_ref_record(direct, fhdr, ent);
    return FULLUPDATE;
}

static int
do_add_recommend(const char *direct, fileheader_t *fhdr,
         int ent, const char *buf, int type)
{
    char    path[PATHLEN];
    int     update = 0;
    /*
      race here:
      為了減少 system calls , 現在直接用當前的推文數 +1 寫入 .DIR 中.
      造成
      1.若該文檔名被換掉的話, 推文將寫至舊檔名中 (造成幽靈檔)
      2.沒有重新讀一次, 所以推文數可能被少算
      3.若推的時候前文被刪, 將加到後文的推文數

     */
    setdirpath(path, direct, fhdr->filename);
    if( log_file(path, 0, buf) == -1 ){ // 不 CREATE
    vmsg("推薦失敗");
    return -1;
    }

    // XXX do lock some day!

    /* This is a solution to avoid most racing (still some), but cost four
     * system calls.                                                        */

    if(type == RECTYPE_GOOD && fhdr->recommend < MAX_RECOMMENDS )
          update = 1;
    else if(type == RECTYPE_BAD && fhdr->recommend > -MAX_RECOMMENDS)
          update = -1;
    fhdr->recommend += update;

    // since we want to do 'modification'...
    fhdr->modified = dasht(path);

    if (fhdr->modified > 0)
    {
    if (modify_dir_lite(direct, ent, fhdr->filename,
        fhdr->modified, NULL, update) < 0)
        return -1;
    // mark my self as "read this file".
    brc_addlist(fhdr->filename, fhdr->modified);
    }
    
    return 0;
}

int
recommend(int ent, fileheader_t * fhdr, const char *direct)
{
    char            buf[PATHLEN], msg[STRLEN];
    const char      *myid = cuser.userid;
    char        aligncmt = 0;
    char        mynick[IDLEN+1];
#ifndef OLDRECOMMEND
    static const char *ctype[RECTYPE_SIZE] = {
               "推", "噓", "→", 
           };
    static const char *ctype_attr[RECTYPE_SIZE] = {
               ANSI_COLOR(1;33),
               ANSI_COLOR(1;31),
               ANSI_COLOR(1;37),
           }, *ctype_attr2[RECTYPE_SIZE] = {
               ANSI_COLOR(1;37),
               ANSI_COLOR(1;31),
               ANSI_COLOR(1;31),
           }, *ctype_long[RECTYPE_SIZE] = {
               "值得推薦",
               "給它噓聲",
               "只加→註解",
           };
#endif
    int             type, maxlength;
    boardheader_t  *bp;
    static time4_t  lastrecommend = 0;
    static int lastrecommend_bid = -1;
    static char lastrecommend_fname[FNLEN] = "";
    int isGuest = (strcmp(cuser.userid, STR_GUEST) == EQUSTR);
    int logIP = 0;
    int ymsg = b_lines -1;

    if (!fhdr || !fhdr->filename[0])
    return DONOTHING;

    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    bp = getbcache(currbid);
    if (bp->brdattr & BRD_NORECOMMEND || fhdr->filename[0] == 'L' || 
        ((fhdr->filemode & FILE_MARKED) && (fhdr->filemode & FILE_SOLVED))) {
    vmsg("抱歉, 禁止推薦");
    return FULLUPDATE;
    }
    if (   !CheckPostPerm() || isGuest)
    {
    vmsg("您權限不足, 無法推薦!"); //  "(可按大寫 I 查看限制)"
    return FULLUPDATE;
    }
    if ((bp->brdattr & BRD_VOTEBOARD) || (fhdr->filemode & FILE_VOTE))
    {
    do_voteboardreply(fhdr);
    return FULLUPDATE;
    }

#ifdef SAFE_ARTICLE_DELETE
    if (fhdr->filename[0] == '.') {
    vmsg("本文已刪除");
    return FULLUPDATE;
    }
#endif

#ifndef DEBUG
    if (!CheckPostRestriction(currbid))
    {
    vmsg("你不夠資深喔! (可按 i 查看限制)");
    return FULLUPDATE;
    }
#endif

    aligncmt = (bp->brdattr & BRD_ALIGNEDCMT) ? 1 : 0;
    if((currmode & MODE_BOARD) || HasUserPerm(PERM_SYSOP))
    {
    /* I'm BM or SYSOP. */
    } 
    else if (bp->brdattr & BRD_NOFASTRECMD) 
    {
    int d = (int)bp->fastrecommend_pause - (now - lastrecommend);
    if (d > 0)
    {
        vmsgf("本板禁止快速連續推文,請再等 %d 秒", d);
        return FULLUPDATE;
    }
    }
    {
    // kcwu
    static unsigned char lastrecommend_minute = 0;
    static unsigned short recommend_in_minute = 0;
    unsigned char now_in_minute = (unsigned char)(now / 60);
    if(now_in_minute != lastrecommend_minute) {
        recommend_in_minute = 0;
        lastrecommend_minute = now_in_minute;
    }
    recommend_in_minute++;
    if(recommend_in_minute>60) {
        vmsg("系統禁止短時間內大量推文");
        return FULLUPDATE;
    }
    }
    {
    // kcwu
    char path[PATHLEN];
    off_t size;
    setdirpath(path, direct, fhdr->filename);
    size = dashs(path);
    if (size > 5*1024*1024) {
        vmsg("檔案太大, 無法繼續推文, 請另撰文發表");
        return FULLUPDATE;
    }

    if (size > 100*1024) {
        int d = 10 - (now - lastrecommend);
        if (d > 0) {
        vmsgf("本文已過長, 禁止快速連續推文, 請再等 %d 秒", d);
        return FULLUPDATE;
        }
    }
    }
            

#ifdef USE_COOLDOWN
       if(check_cooldown(bp))
      return FULLUPDATE;
#endif

    type = RECTYPE_GOOD;

    // why "recommend == 0" here?
    // some users are complaining that they like to fxck up system
    // with lots of recommend one-line text.
    // since we don't support recognizing update of recommends now,
    // they tend to use the counter to identify whether an arcitle
    // has new recommends or not.
    // so, make them happy here.
#ifndef OLDRECOMMEND
    // no matter it is first time or not.
    if (strcmp(cuser.userid, fhdr->owner) == 0)
#else
    // old format is one way counter, so let's relax.
    if (fhdr->recommend == 0 && strcmp(cuser.userid, fhdr->owner) == 0)
#endif
    {
    // owner recommend
    type = RECTYPE_ARROW;
    move(ymsg--, 0); clrtoeol();
#ifndef OLDRECOMMEND
    outs("作者本人, 使用 → 加註方式\n");
#else
    outs("作者本人首推, 使用 → 加註方式\n");
#endif

    }
#ifndef DEBUG
    else if (!(currmode & MODE_BOARD) && 
        (now - lastrecommend) < (
#if 0
        /* i'm not sure whether this is better or not */
        (bp->brdattr & BRD_NOFASTRECMD) ? 
         bp->fastrecommend_pause :
#endif
        90))
    {
    // too close
    type = RECTYPE_ARROW;
    move(ymsg--, 0); clrtoeol();
    outs("時間太近, 使用 → 加註方式\n");
    }
#endif

#ifndef OLDRECOMMEND
    else
    {
    int i;

    move(b_lines, 0); clrtoeol();
    outs(ANSI_COLOR(1)  "您覺得這篇文章 ");

    for (i = 0; i < RECTYPE_SIZE; i++)
    {
        if (i == RECTYPE_BAD && (bp->brdattr & BRD_NOBOO))
        continue;
        outs(ctype_attr[i]);
        prints("%d.", i+1);
        outs(ctype_long[i]);
        outc(' ');
    }
    prints(ANSI_RESET "[%d]? ",
        RECTYPE_DEFAULT+1);

    type = igetch();

    if (!isascii(type) || !isdigit(type))
    {
        type = RECTYPE_DEFAULT;
    } else {
        type -= '1';
        if (type < 0 || type > RECTYPE_MAX)
        type = RECTYPE_DEFAULT;
    }

    if( (bp->brdattr & BRD_NOBOO) && (type == RECTYPE_BAD))
        type = RECTYPE_ARROW;
    assert(type >= 0 && type <= RECTYPE_MAX);

    move(b_lines, 0); clrtoeol();
    }
#endif

    // warn if article is outside post
    if (strchr(fhdr->owner, '.')  != NULL)
    {
    move(ymsg--, 0); clrtoeol();
    outs(ANSI_COLOR(1;31) 
        "◆這篇文章來自暱名板或外站轉信板,原作者可能無法看到推文。" 
        ANSI_RESET "\n");
    }

    // warn if in non-standard mode
    {
    char *p = strrchr(direct, '/');
    // allow .DIR or .DIR.bottom
    if (!p || strncmp(p+1, FN_DIR, strlen(FN_DIR)) != 0)
    {
        ymsg --;
        move(ymsg--, 0); clrtoeol();
        outs(ANSI_COLOR(1;33) 
        "◆您正在搜尋(標題、作者...)或其它特殊列表模式,"
        "推文計數與修改記錄將會分開計算。" 
        ANSI_RESET "\n"
        "  若想正常計數請先左鍵退回正常列表模式。\n");
    }
    }

    if(type >  RECTYPE_MAX || type < 0)
    type = RECTYPE_ARROW;

    maxlength = 78 - 
    3 /* lead */ - 
    6 /* date */ - 
    1 /* space */ -
    6 /* time */;

    if (bp->brdattr & BRD_IPLOGRECMD || isGuest)
    {
    maxlength -= 15 /* IP */;
    logIP = 1;
    }

#if defined(PLAY_ANGEL) && defined(BN_ANGELPRAY) && defined(ANGEL_ANONYMOUS_COMMENT)
    if (HasUserPerm(PERM_ANGEL) && currboard && strcmp(currboard, BN_ANGELPRAY) == 0 &&
    vans("要使用小天使暱名推文嗎? [Y/n]: ") != 'n')
    {
    // angel push
    mynick[0] = 0;
    angel_load_my_fullnick(mynick, sizeof(mynick));
    myid = mynick;
    }
#endif 

    if (aligncmt)
    {
    // left align, voted by LydiaWu and LadyNotorious
    snprintf(buf, sizeof(buf), "%-*s", IDLEN, myid);
    strlcpy(mynick, buf, sizeof(mynick));
    myid = mynick;
    }

#ifdef OLDRECOMMEND
    maxlength -= 2; /* '推' */
    maxlength -= strlen(myid);
    sprintf(buf, "%s %s:", "→" , myid);

#else // !OLDRECOMMEND
    maxlength -= strlen(myid);
# ifdef    USE_PFTERM
    sprintf(buf, "%s%s%s %s:", 
        ctype_attr[type], ctype[type], ANSI_RESET, myid);
# else  // !USE_PFTERM
    sprintf(buf, "%s %s:", ctype[type], myid);
# endif // !USE_PFTERM
#endif // !OLDRECOMMEND

    move(b_lines, 0);
    clrtoeol();

    if (!getdata(b_lines, 0, buf, msg, maxlength, DOECHO))
    return FULLUPDATE;

    // make sure to do modification
    {
    char ans[2];
    sprintf(buf+strlen(buf), 
#ifdef USE_PFTERM
        ANSI_REVERSE "%-*s" ANSI_RESET " 確定[y/N]:", 
#else
        "%-*s 確定[y/N]:", 
#endif
        maxlength, msg);
    move(b_lines, 0);
    clrtoeol();
    if(!getdata(b_lines, 0, buf, ans, sizeof(ans), LCECHO) ||
        ans[0] != 'y')
        return FULLUPDATE;
    }

    // log if you want
#ifdef LOG_PUSH
    {
    static  int tolog = 0;
    if( tolog == 0 )
        tolog =
        (cuser.numlogindays < 50 || (now - cuser.firstlogin) < DAY_SECONDS * 7)
        ? 1 : 2;
    if( tolog == 1 ){
        FILE   *fp;
        if( (fp = fopen("log/push", "a")) != NULL ){
        fprintf(fp, "%s %d %s %s %s\n", cuser.userid, 
            (int)now, currboard, fhdr->filename, msg);
        fclose(fp);
        }
        sleep(1);
    }
    }
#endif // LOG_PUSH

    STATINC(STAT_RECOMMEND);

    {
    /* build tail first. */
    char tail[STRLEN];

    if(logIP)
    {
        snprintf(tail, sizeof(tail),
            "%15s %s",
            FROMHOST, 
            Cdate_mdHM(&now));
    } else {
        snprintf(tail, sizeof(tail),
            " %s",
            Cdate_mdHM(&now));
    }

#ifdef OLDRECOMMEND
    snprintf(buf, sizeof(buf),
        ANSI_COLOR(1;31) "→ " ANSI_COLOR(33) "%s" 
        ANSI_RESET ANSI_COLOR(33) ":%-*s" ANSI_RESET
        "推%s\n",
        myid, maxlength, msg, tail);
#else
    snprintf(buf, sizeof(buf),
        "%s%s " ANSI_COLOR(33) "%s" ANSI_RESET ANSI_COLOR(33) 
        ":%-*s" ANSI_RESET "%s\n",
             ctype_attr2[type], ctype[type], myid, 
         maxlength, msg, tail);
#endif // OLDRECOMMEND
    }

    do_add_recommend(direct, fhdr,  ent, buf, type);
    lastrecommend = now;
    lastrecommend_bid = currbid;
    strlcpy(lastrecommend_fname, fhdr->filename, sizeof(lastrecommend_fname));
    return FULLUPDATE;
}

static int
mark_post(int ent, fileheader_t * fhdr, const char *direct)
{
    char buf[STRLEN], fpath[STRLEN];

    if (!(currmode & MODE_BOARD))
    return DONOTHING;

    setbpath(fpath, currboard);
    sprintf(buf, "%s/%s", fpath, fhdr->filename);

    if( !(fhdr->filemode & FILE_MARKED) && /* 若目前還沒有 mark 才要 check */
    access(buf, F_OK) < 0 )
    return DONOTHING;

    fhdr->filemode ^= FILE_MARKED;
    substitute_ref_record(direct, fhdr, ent);
    check_locked(fhdr);
    return PART_REDRAW;
}

int
del_range(int ent, const fileheader_t *fhdr, const char *direct)
{
    char            num1[8], num2[8];
    int             inum1, inum2;
    boardheader_t  *bp = NULL;

    /* 有三種情況會進這裡, 信件, 看板, 精華區 */

    if( direct[0] != 'h' && currbid) /* 信件不用 check */
    { 
    // 很不幸的是有一種是信件->mail_cite->精華區
        bp = getbcache(currbid);
    if (is_readonly_board(bp->brdname))
        return DONOTHING;
    }

    /* rocker.011018: 串接模式下還是不允許刪除比較好 */
    if (currmode & MODE_SELECT) {
    vmsg("請先回到正常模式後再進行刪除...");
    return FULLUPDATE;
    }

    if ((currstat != READING) || (currmode & MODE_BOARD)) {
    getdata(1, 0, "[設定刪除範圍] 起點:", num1, 6, DOECHO);
    inum1 = atoi(num1);
    if (inum1 <= 0) {
        vmsg("起點有誤");
        return FULLUPDATE;
    }
    getdata(1, 28, "終點:", num2, 6, DOECHO);
    inum2 = atoi(num2);
    if (inum2 < inum1) {
        vmsg("終點有誤");
        return FULLUPDATE;
    }
    getdata(1, 48, msg_sure_ny, num1, 3, LCECHO);
    if (*num1 == 'y') {
        int ret = 0;
        outmsg("處理中,請稍後...");
        refresh();
#ifdef SAFE_ARTICLE_DELETE
        if(bp && !(currmode & MODE_DIGEST) && bp->nuser > 30)
        ret = safe_article_delete_range(direct, inum1, inum2);
        else
#endif
        ret = delete_range(direct, inum1, inum2);
        if (ret < 0)
        {
        clear();
        vs_hdr("刪除失敗");
        outs("\n\n無法刪除檔案。可能是同時有其它人也在進行刪除。\n\n"
             "若此錯誤持續發生,請等約一小時後再重試。\n\n"
             "若到時仍無法刪除,請到 " BN_SYSOP " 看板報告。\n");
        vmsg("無法刪除。可能有其它人正在同時刪除。");
        return FULLUPDATE;
        } else 
        fixkeep(direct, inum1);

        if ((curredit & EDIT_MAIL)==0 && (currmode & MODE_BOARD)) // Ptt:update cache
        setbtotal(currbid);
            else if(currstat == RMAIL)
                setupmailusage();

        return DIRCHANGED;
    }
    return FULLUPDATE;
    }
    return DONOTHING;
}

static int
del_post(int ent, fileheader_t * fhdr, char *direct)
{
#ifdef SAFE_ARTICLE_DELETE
    char        reason[PROPER_TITLE_LEN];
#endif
    char            genbuf[100], newpath[PATHLEN];
    int             not_owned, is_anon, tusernum, del_ok = 0;
    boardheader_t  *bp;

    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    bp = getbcache(currbid);

    if (is_readonly_board(bp->brdname))
    return DONOTHING;

    /* TODO recursive lookup */
    if (currmode & MODE_SELECT) { 
        vmsg("請回到一般模式再刪除文章");
        return DONOTHING;
    }

    if ((fhdr->filemode & FILE_BOTTOM) || 
       (fhdr->filemode & FILE_MARKED) || (fhdr->filemode & FILE_DIGEST) ||
    (fhdr->owner[0] == '-'))
    return DONOTHING;

    is_anon = (fhdr->filemode & FILE_ANONYMOUS);
    if(is_anon)
    /* When the file is anonymous posted, fhdr->multi.anon_uid is author.
     * see do_general() */
        tusernum = fhdr->multi.anon_uid;
    else
        tusernum = searchuser(fhdr->owner, NULL);

    not_owned = (tusernum == usernum ? 0: 1);
    if ((!(currmode & MODE_BOARD) && not_owned) ||
    ((bp->brdattr & BRD_VOTEBOARD) && !HasUserPerm(PERM_SYSOP)) ||
    !strcmp(cuser.userid, STR_GUEST))
    return DONOTHING;

    if (fhdr->filename[0]=='L') fhdr->filename[0]='M';

#ifdef SAFE_ARTICLE_DELETE
    reason[0] = 0;
    // query if user really wants to delete it
    if (not_owned && !is_anon && fhdr->owner[0])
    {
    // manager (bm, sysop, police)
    do {
        getdata(1, 0, "請確定刪除(Y/N/R加註理由)?[N]", genbuf, 3, LCECHO);

        // for y/n, skip.
        if (genbuf[0] != 'r')
        break;

        // build reason string (based on STR_SAFEDEL_TITLE)
        snprintf(reason, sizeof(reason), "(已被%s刪除) <%s>", 
            cuser.userid, fhdr->owner);
        move(3, 0); clrtoeol();
        getdata_str(2, 0, " >> 請輸入刪除後要顯示的標題: □ ", 
            reason, sizeof(reason), DOECHO, reason);

        if (!reason[0])
        {
        vmsg("未輸入理由,放棄刪除。");
        genbuf[0] = 'n';
        break;
        }

        // confirm again!
        move(4, 0); clrtoeol();
        getdata(3, 0, "請再次確定是否要用上述理由刪除(Y/N)?[N]", 
            genbuf, 3, LCECHO);

        // since the default y/n is same to msg_del_ny, we reuse the genbuf[0] here.
    } while (0);
    } else 
#endif
    {
    getdata(1, 0, msg_del_ny, genbuf, 3, LCECHO);
    }
    if (genbuf[0] == 'y') {
    if(
#ifdef SAFE_ARTICLE_DELETE
       ((reason[0] || bp->nuser > 30) && !(currmode & MODE_DIGEST) &&
            !safe_article_delete(ent, fhdr, direct, reason[0] ? reason : NULL)) ||
#endif
       // XXX TODO delete_record is really really dangerous - 
       // we should verify the header (maybe by filename) is the same.
       // currently race condition is easily cause by 2 BMs
       !delete_record(direct, sizeof(fileheader_t), ent)
       ) {

        del_ok = (cancelpost(fhdr, not_owned, newpath) == 0) ? 1 : 0;
            deleteCrossPost(fhdr, bp->brdname);
#ifdef ASSESS
#define SIZE    sizeof(badpost_reason) / sizeof(char *)

        // badpost assignment

        // case one, self-owned, invalid author, or digest mode - should not give bad posts
        if (!not_owned || tusernum <= 0 || (currmode & MODE_DIGEST) )
        {
        // do nothing
        } 
        // case 2, got error in file deletion (already deleted, also skip badpost)
        else if (!del_ok)
        {
        move(1, 40); clrtoeol();
        outs("此檔已被別人刪除(跳過劣文設定)");
        pressanykey();
        }
        // case 3, post older than one week (TODO use macro for the duration)
        else if (now - atoi(fhdr->filename + 2) > 7 * 24 * 60 * 60)
        {
        move(1, 40); clrtoeol();
        outs("文章超過一週(跳過劣文設定)");
        pressanykey();
        }
        // case 4, can assign badpost
        else 
        {
        // TODO not_owned 時也要改變 numpost?
        getdata(1, 40, "惡劣文章?(y/N)", genbuf, 3, LCECHO);

        if (genbuf[0]=='y') {
            int i;
            char *userid=getuserid(tusernum);
 
            move(b_lines - 2, 0);
            clrtobot();
            for (i = 0; i < SIZE; i++)
            prints("%d.%s ", i + 1, badpost_reason[i]);
            prints("%d.%s", i + 1, "其他");
            getdata(b_lines - 1, 0, "請選擇[0:取消劣文]:", genbuf, 3, LCECHO);
            i = genbuf[0] - '1';
            if (i >= 0 && i < SIZE)
                sprintf(genbuf,"劣文退回(%s)", badpost_reason[i]);
                    else if(i==SIZE)
                       {
                strcpy(genbuf,"劣文退回(");
                getdata_buf(b_lines, 0, "請輸入原因", genbuf+9, 
                                 50, DOECHO);
                        strcat(genbuf,")");
                       }
                    if(i>=0 && i <= SIZE)
            {
                      strncat(genbuf, fhdr->title, 64-strlen(genbuf)); 

#ifdef USE_COOLDOWN
                      add_cooldowntime(tusernum, 60);
                      add_posttimes(tusernum, 15); //Ptt: 凍結 post for 1 hour
#endif

              if (!(inc_badpost(userid, 1) % 5)){
                        userec_t xuser;
            post_violatelaw(userid, BBSMNAME " 系統警察", 
                "劣文累計 5 篇", "罰單一張");
            mail_violatelaw(userid, BBSMNAME " 系統警察", 
                "劣文累計 5 篇", "罰單一張");
                        kick_all(userid);
                        passwd_sync_query(tusernum, &xuser);
                        xuser.money = moneyof(tusernum);
                        xuser.vl_count++;
                xuser.userlevel |= PERM_VIOLATELAW;
            xuser.timeviolatelaw = now;  
            passwd_sync_update(tusernum, &xuser);
               }
               sendalert(userid,  ALERT_PWD_PERM);
               mail_id(userid, genbuf, newpath, cuser.userid);

#ifdef BAD_POST_RECORD
             {
              int rpt_bid = getbnum(BAD_POST_RECORD);
                      if (rpt_bid > 0) {
              fileheader_t report_fh;
              char report_path[PATHLEN];

              setbpath(report_path, BAD_POST_RECORD);
              stampfile(report_path, &report_fh);

              strcpy(report_fh.owner, "[" BBSMNAME "警察局]");
              snprintf(report_fh.title, sizeof(report_fh.title),
                  "%s 板 %s 板主給予 %s 一篇劣文",
                  currboard, cuser.userid, userid);
              Copy(newpath, report_path);

              setbdir(report_path, BAD_POST_RECORD);
              append_record(report_path, &report_fh, sizeof(report_fh));
 
                          touchbtotal(rpt_bid);
              }
             }
#endif /* defined(BAD_POST_RECORD) */
           }
                }
        }
#undef SIZE
#endif

        setbtotal(currbid);

        // 扣錢前先把文章種類搞清楚
        // freebn/brd_bad: should be done before, but let's make it safer.
        // new rule: only articles with money need updating
        // numpost (to solve deleting cross-posts).
        // DIGEST mode 不用管
        // INVALIDMONEY_MODES (FILE_BID, FILE_ANONYMOUS, ...) 也都不用扣
        if (fhdr->multi.money < 0 || 
        IsFreeBoardName(currboard) || (currbrdattr & BRD_BAD) ||
        (currmode & MODE_DIGEST) ||
        (fhdr->filemode & INVALIDMONEY_MODES) ||
        0)
        fhdr->multi.money = 0;

        // XXX also check MAX_POST_MONEY in case any error results in bad money...
        if (fhdr->multi.money <= 0 || fhdr->multi.money > MAX_POST_MONEY)
        {
        // no need to change user record
        } 
        else if (not_owned)
        {
        // not owner case
        if (tusernum)
        {
            userec_t xuser;
            assert(tusernum != usernum);
            // TODO we're doing redundant i/o here... merge and refine someday
            if (passwd_sync_query(tusernum, &xuser) == 0) {
            if (xuser.numposts > 0)
                xuser.numposts--;
            passwd_sync_update(tusernum, &xuser);
            }
            deumoney(tusernum, -fhdr->multi.money);
            sendalert_uid(tusernum, ALERT_PWD_PERM);
#ifdef USE_COOLDOWN
            if (bp->brdattr & BRD_COOLDOWN)
            add_cooldowntime(tusernum, 15);
#endif
        }
        } 
        else
        {
        // owner case
        pwcuDecNumPost();
        demoney(-fhdr->multi.money);
        sendalert(cuser.userid, ALERT_PWD_PERM);
        vmsgf("您的文章減為 %d 篇,支付清潔費 %d 元", 
            cuser.numposts, fhdr->multi.money);
        }

        return DIRCHANGED;
    } // delete_record
    } // genbuf[0] == 'y'
    return FULLUPDATE;
}

static int  // Ptt: 修石頭文   
show_filename(int ent, const fileheader_t * fhdr, const char *direct)
{
    if(!HasUserPerm(PERM_SYSOP)) return DONOTHING;
    vmsgf("檔案名稱: %s ", fhdr->filename);
    return PART_REDRAW;
}

static int
lock_post(int ent, fileheader_t * fhdr, const char *direct)
{
    char fn1[PATHLEN];
    char genbuf[PATHLEN] = "";
    int i;
    boardheader_t *bp = NULL;
    
    if (currstat == RMAIL)
    return DONOTHING;

    if (!(currmode & MODE_BOARD) && !HasUserPerm(PERM_SYSOP | PERM_POLICE))
    return DONOTHING;

    bp = getbcache(currbid);
    assert(bp);

    if (fhdr->filename[0]=='M') {
    if (!HasUserPerm(PERM_SYSOP | PERM_POLICE))
        return DONOTHING;

    getdata(b_lines - 1, 0, "請輸入鎖定理由:", genbuf, 50, DOECHO);

    if (vans("要將文章鎖定嗎(y/N)?") != 'y')
        return FULLUPDATE;
        setbfile(fn1, currboard, fhdr->filename);
        fhdr->filename[0] = 'L';
    syncnow();
    bp->SRexpire = now;
    }
    else if (fhdr->filename[0]=='L') {
    if (vans("要將文章鎖定解除嗎(y/N)?") != 'y')
        return FULLUPDATE;
        fhdr->filename[0] = 'M';
        setbfile(fn1, currboard, fhdr->filename);
    syncnow();
    bp->SRexpire = now;
    }
    substitute_ref_record(direct, fhdr, ent);
    post_policelog(currboard, fhdr->title, "鎖文", genbuf, fhdr->filename[0] == 'L' ? 1 : 0);
    if (fhdr->filename[0] == 'L') {
    fhdr->filename[0] = 'M';
    do_crosspost("PoliceLog", fhdr, fn1, 0);
    fhdr->filename[0] = 'L';
    snprintf(genbuf, sizeof(genbuf), "%s 板遭鎖定文章 - %s", currboard, fhdr->title);
    for (i = 0; i < MAX_BMs && SHM->BMcache[currbid-1][i] != -1; i++)
        mail_id(SHM->userid[SHM->BMcache[currbid-1][i] - 1], genbuf, fn1, "[系統]");
    }
    return FULLUPDATE;
} 

static int
view_postinfo(int ent, const fileheader_t * fhdr, const char *direct, int crs_ln)
{
    aidu_t aidu = 0;
    int l = crs_ln + 3;  /* line of cursor */
    int area_l = l + 1;
#ifdef QUERY_ARTICLE_URL
    const int area_lines = 5;
#else
    const int area_lines = 4;
#endif

    if(!fhdr || fhdr->filename[0] == '.' || !fhdr->filename[0])
      return DONOTHING;

    if((area_l + area_lines > b_lines) ||  /* 下面放不下 */
       (l >= (b_lines  * 2 / 3)))  /* 略超過畫面 2/3 */
      area_l -= (area_lines + 1);

    grayout(0, MIN(l - 1, area_l)-1, GRAYOUT_DARK);
    grayout(MAX(l + 1 + 1, area_l + area_lines), b_lines-1, GRAYOUT_DARK);
    grayout(l, l, GRAYOUT_BOLD);

    /* 清除文章的前一行或後一行 */
    if(area_l > l)
      move(l - 1, 0);
    else
      move(l + 1, 0);
    clrtoeol();

    move(area_l-(area_l < l), 0);
    clrtoln(area_l -(area_l < l) + area_lines+1);
    outc(' '); outs(ANSI_CLRTOEND);
    move(area_l -(area_l < l) + area_lines, 0); 
    outc(' '); outs(ANSI_CLRTOEND);
    move(area_l, 0);

    // TODO XXX support wide terminal someday.

    prints("┌─────────────────────────────────────┐\n");

    aidu = fn2aidu((char *)fhdr->filename);
    if(aidu > 0)
    {
      char aidc[10];
      int y, x;
      
      aidu2aidc(aidc, aidu);
      prints("│ " AID_DISPLAYNAME ": " 
      ANSI_COLOR(1) "#%s" ANSI_RESET " (%s) [%s] ", 
      aidc, currboard && currboard[0] ? currboard : "未知",
      AID_HOSTNAME);
      getyx_ansi(&y, &x);
      x = 75 - x;
      if (x > 1)
      prints("%.*s ", x, fhdr->title);
      outs("\n");
    }
    else
    {
      prints("│\n");
    }

#ifdef QUERY_ARTICLE_URL
    {
    boardheader_t *bp = NULL;

    // XXX currbid should match currboard, right? can we use it?
    if (currboard && currboard[0])
    {
        int bnum = getbnum(currboard);
        if (bnum > 0)
        {
        assert(0<=bnum-1 && bnum-1<MAX_BOARD);
        bp = getbcache(bnum);
        }
    }

    if (!bp)
    {
        prints("│\n");
    } 
    else if ((bp->brdattr & (BRD_HIDE | BRD_OVER18)) ||
         (bp->level && !(bp->brdattr & BRD_POSTMASK)) // !POSTMASK = restricted read
        )
    {
        // over18 boards do not provide URL.
        prints("│ 本看板目前不提供" URL_DISPLAYNAME " \n");
    }
    else
    {
        prints("│ " URL_DISPLAYNAME ": " 
            ANSI_COLOR(1) URL_PREFIX "/%s/%s.html" ANSI_RESET "\n",
            currboard, fhdr->filename);
    }
    }
#endif

    if(fhdr->filemode & FILE_ANONYMOUS)
    /* When the file is anonymous posted, fhdr->multi.anon_uid is author.
     * see do_general() */
    prints("│ 匿名管理編號: %d (同一人號碼會一樣)",
           fhdr->multi.anon_uid + (int)currutmp->pid);
    else {
    int m = query_file_money(fhdr);

    if(m < 0)
        prints("│ 特殊文章,無價格記錄");
    else
        prints("│ 這一篇文章值 %d 元", m);

    }
    prints("\n");
    prints("└─────────────────────────────────────┘\n");

    /* 印對話框的右邊界 */
    {
      int i;

      for(i = 1; i < area_lines - 1; i ++)
      {
        move_ansi(area_l + i , 76);
        prints("│");
      }
    }
    {
        int r = pressanykey();
        /* TODO: 多加一個 LISTMODE_AID? */
    /* QQ: enable money listing mode */
    if (r == 'Q')
    {
        currlistmode = (currlistmode == LISTMODE_MONEY) ?
        LISTMODE_DATE : LISTMODE_MONEY;
        vmsg((currlistmode == LISTMODE_MONEY) ? 
            "開啟文章價格列表模式" : "停止列出文章價格");
    }
     }

    return FULLUPDATE;
}

#ifdef OUTJOBSPOOL
/* 看板備份 */
static int
tar_addqueue(void)
{
    char            email[60], qfn[80], ans[2];
    FILE           *fp;
    char            bakboard, bakman;
    clear();
    showtitle("看板備份", BBSNAME);
    move(2, 0);
    if (!((currmode & MODE_BOARD) || HasUserPerm(PERM_SYSOP))) {
    move(5, 10);
    outs("妳要是板主或是站長才能醬醬啊 -.-\"\"");
    pressanykey();
    return FULLUPDATE;
    }
    snprintf(qfn, sizeof(qfn), BBSHOME "/jobspool/tarqueue.%s", currboard);
    if (access(qfn, 0) == 0) {
    outs("已經排定行程, 稍後會進行備份");
    pressanykey();
    return FULLUPDATE;
    }
#ifdef TARQUEUE_SENDURL
    move (3,0); outs("請輸入通知信箱 (預設為此 BBS 帳號信箱): ");
    if (!getdata_str(4, 2, "",
        email, sizeof(email), DOECHO, cuser.userid))
    return FULLUPDATE;
    if (strstr(email, "@") == NULL)
    {
    strcat(email, ".bbs@");
    strcat(email, MYHOSTNAME);
    }
    move(4,0); clrtoeol();
    outs(email);
#else
    if (!getdata(4, 0, "請輸入目的信箱:", email, sizeof(email), DOECHO))
    return FULLUPDATE;

    /* check email -.-"" */
    if (strstr(email, "@") == NULL || strstr(email, ".bbs@") != NULL) {
    move(6, 0);
    outs("您指定的信箱不正確! ");
    pressanykey();
    return FULLUPDATE;
    }
#endif
    getdata(6, 0, "要備份看板內容嗎(Y/N)?[Y]", ans, sizeof(ans), LCECHO);
    bakboard = (ans[0] == 'n') ? 0 : 1;
    getdata(7, 0, "要備份精華區內容嗎(Y/N)?[N]", ans, sizeof(ans), LCECHO);
    bakman = (ans[0] == 'y') ? 1 : 0;
    if (!bakboard && !bakman) {
    move(8, 0);
    outs("可是我們只能備份看板或精華區的耶 ^^\"\"\"");
    pressanykey();
    return FULLUPDATE;
    }
    fp = fopen(qfn, "w");
    fprintf(fp, "%s\n", cuser.userid);
    fprintf(fp, "%s\n", email);
    fprintf(fp, "%d,%d\n", bakboard, bakman);
    fclose(fp);

    move(10, 0);
    outs("系統已經將您的備份排入行程, \n");
    outs("稍後將會在系統負荷較低的時候將資料寄給您~ :) ");
    pressanykey();
    return FULLUPDATE;
}
#endif

/* ----------------------------------------------------- */
/* 看板進板畫面、文摘、精華區                              */
/* ----------------------------------------------------- */
int
b_note_edit_bname(int bid)
{
    char            buf[PATHLEN];
    int             aborted;
    boardheader_t  *fh = getbcache(bid);
    assert(0<=bid-1 && bid-1<MAX_BOARD);
    setbfile(buf, fh->brdname, fn_notes);
    aborted = veditfile(buf);
    if (aborted == -1) {
    clear();
    outs(msg_cancel);
    pressanykey();
    } else {
       // alert user our new b_note policy.
       char msg[STRLEN];
       clear();
       vs_hdr("進板畫面顯示設定");
       outs("\n"
       "\t請決定是否要在使用者首次進入看板時顯示剛儲存的進板畫面。\n\n"
       "\t請注意若使用者連續重複進出同一個看板時,進板畫面只會顯示一次。\n"
       "\t此為系統設定,並非設定錯誤。\n\n"
       "\t(使用者隨時可按 b 或經由進出不同看板來重新顯示進板畫面)\n");

       // 設定日期的效果其實很早就不會動了,所以拔掉
       snprintf(msg, sizeof(msg), 
           "要在首次進入看板時顯示進板畫面嗎? (y/n) [%c]: ",
           fh->bupdate ? 'Y' : 'N');
       getdata(10, 0, msg, buf, 3, LCECHO);

       switch(buf[0])
       {
           case 'y':
               // assign a large, max date.
               fh->bupdate = INT_MAX -1;
               break;
           case 'n':
               fh->bupdate = 0;
               break;
           default:
           // do nothing
               break;
       }
       // expire BM's lastbid to help him verify settings.
       bnote_lastbid = -1;

       assert(0<=bid-1 && bid-1<MAX_BOARD);
       substitute_record(fn_board, fh, sizeof(boardheader_t), bid);
    }
    return 0;
}

static int
b_notes_edit(void)
{
    if (currmode & MODE_BOARD) {
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    b_note_edit_bname(currbid);
    return FULLUPDATE;
    }
    return 0;
}

static int
b_notes(void)
{
    char            buf[PATHLEN];
    int mr = 0;

    setbfile(buf, currboard, fn_notes);
    mr = more(buf, NA);

    if (mr == -1)
    {
    clear();
    move(4, 20);
    outs("本看板尚無進板畫面。");
    }
    if(mr != READ_NEXT)
        pressanykey();
    return FULLUPDATE;
}

int
board_select(void)
{
    currmode &= ~MODE_SELECT;
    currsrmode = 0;
    if (currstat == RMAIL)
    sethomedir(currdirect, cuser.userid);
    else
    setbdir(currdirect, currboard);
    return NEWDIRECT;
}

int
board_digest(void)
{
    if (currmode & MODE_SELECT)
    board_select();
    currmode ^= MODE_DIGEST;

    // MODE_POST may be changed if board is modified.
    // do not change post perm here. use other instead.

    setbdir(currdirect, currboard);
    return NEWDIRECT;
}


static int
push_bottom(int ent, fileheader_t *fhdr, const char *direct)
{
    int num;
    char buf[PATHLEN];
    if ((currmode & MODE_DIGEST) || !(currmode & MODE_BOARD)
        || fhdr->filename[0]=='L')
        return DONOTHING;
    setbottomtotal(currbid);  // <- Ptt : will be remove when stable
    num = getbottomtotal(currbid);
    if (!(fhdr->filemode & FILE_BOTTOM))
    {
    move(b_lines-1, 0); clrtoeol();
    outs(ANSI_COLOR(1;33) "提醒您置底與原文目前互為連結,刪掉原文也會導致置底消失。" ANSI_RESET);
    }
    if( vans(fhdr->filemode & FILE_BOTTOM ?
           "取消置底公告?(y/N)":
           "加入置底公告?(y/N)") != 'y' )
    return FULLUPDATE;
    if(!(fhdr->filemode & FILE_BOTTOM) ){
          snprintf(buf, sizeof(buf), "%s.bottom", direct);
          if(num >= 5){
              vmsg("不得超過 5 篇重要公告 請精簡!");
              return FULLUPDATE;
      }
      fhdr->filemode ^= FILE_BOTTOM;
      fhdr->multi.refer.flag = 1;
          fhdr->multi.refer.ref = ent;
          append_record(buf, fhdr, sizeof(fileheader_t)); 
    }
    else{
    fhdr->filemode ^= FILE_BOTTOM;
    num = delete_record(direct, sizeof(fileheader_t), ent);
    }
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    setbottomtotal(currbid);
    return DIRCHANGED;
}

static int
good_post(int ent, fileheader_t * fhdr, const char *direct)
{
    char            genbuf[200];
    char            genbuf2[200];
    int             delta = 0;

    if ((currmode & MODE_DIGEST) || !(currmode & MODE_BOARD))
    return DONOTHING;

    if(vans(fhdr->filemode & FILE_DIGEST ? 
              "取消看板文摘?(Y/n)" : "收入看板文摘?(Y/n)") == 'n')
    return READ_REDRAW;

    if (fhdr->filemode & FILE_DIGEST) {
    fhdr->filemode = (fhdr->filemode & ~FILE_DIGEST);
    if (!strcmp(currboard, BN_NOTE) || 
#ifdef BN_ARTDSN        
        !strcmp(currboard, BN_ARTDSN) || 
#endif
        !strcmp(currboard, BN_BUGREPORT) ||
        !strcmp(currboard, BN_LAW)
        ) 
    {
        deumoney(searchuser(fhdr->owner, NULL), -1000); // TODO if searchuser() return 0
        if (!(currmode & MODE_SELECT))
        fhdr->multi.money -= 1000;
        else
        delta = -1000;
    }
    } else {
    fileheader_t    digest;
    char           *ptr, buf[PATHLEN];

    memcpy(&digest, fhdr, sizeof(digest));
    digest.filename[0] = 'G';
    strlcpy(buf, direct, sizeof(buf));
    ptr = strrchr(buf, '/');
    assert(ptr);
    ptr++;
    ptr[0] = '\0';
    snprintf(genbuf, sizeof(genbuf), "%s%s", buf, digest.filename);

    if (dashf(genbuf))
        unlink(genbuf);

    digest.filemode = 0;
    snprintf(genbuf2, sizeof(genbuf2), "%s%s", buf, fhdr->filename);
    Copy(genbuf2, genbuf);
    strcpy(ptr, fn_mandex);
    append_record(buf, &digest, sizeof(digest));

#ifdef BN_DIGEST
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    if(!(getbcache(currbid)->brdattr & BRD_HIDE)) { 
          getdata(1, 0, "好文值得出版到全站文摘?(N/y)", genbuf2, 3, LCECHO);
          if(genbuf2[0] == 'y')
          do_crosspost(BN_DIGEST, &digest, genbuf, 1);
        }
#endif

    fhdr->filemode = (fhdr->filemode & ~FILE_MARKED) | FILE_DIGEST;
    if (!strcmp(currboard, BN_NOTE) || 
#ifdef BN_ARTDSN        
        !strcmp(currboard, BN_ARTDSN) || 
#endif
        !strcmp(currboard, BN_BUGREPORT) ||
        !strcmp(currboard, BN_LAW)
        ) 
    {
        deumoney(searchuser(fhdr->owner, NULL), 1000); // TODO if searchuser() return 0
        if (!(currmode & MODE_SELECT))
        fhdr->multi.money += 1000;
        else
        delta = 1000;
    }
    }
    substitute_ref_record(direct, fhdr, ent);
    return FULLUPDATE;
}

static int
b_help(void)
{
    show_helpfile(fn_boardhelp);
    return FULLUPDATE;
}

#ifdef USE_COOLDOWN

int check_cooldown(boardheader_t *bp)
{
    int diff = cooldowntimeof(usernum) - now; 
    int i, limit[8] = {4000,1,2000,2,1000,3,-1,10};

    if(diff<0)
    SHM->cooldowntime[usernum - 1] &= 0xFFFFFFF0;
    else if( !((currmode & MODE_BOARD) || HasUserPerm(PERM_SYSOP)))
    {
      if( bp->brdattr & BRD_COOLDOWN )
       {
     vmsgf("冷靜一下吧! (限制 %d 分 %d 秒)", diff/60, diff%60);
     return 1;
       }
      else if(posttimesof(usernum)==0xf)
      {
     vmsgf("對不起,您被設劣文! (限制 %d 分 %d 秒)", diff/60, diff%60);
     return 1;
      }
#ifdef NO_WATER_POST
      else
      {
        for(i=0; i<4; i++)
          if(bp->nuser>limit[i*2] && posttimesof(usernum)>=limit[i*2+1])
          {
        vmsgf("對不起,您的文章或推文間隔太近囉! (限制 %d 分 %d 秒)", 
          diff/60, diff%60);
        return 1;
          }
      }
#endif // NO_WATER_POST
   }
   return 0;
}
/**
 * 設定看板冷靜功能, 限制使用者發文時間
 */
static int
change_cooldown(void)
{
    char genbuf[256] = {'\0'};
    boardheader_t *bp = getbcache(currbid);
    
    if (!(HasUserPerm(PERM_SYSOP | PERM_POLICE) || 
        (HasUserPerm(PERM_SYSSUPERSUBOP) && GROUPOP())))
    return DONOTHING;

    if (bp->brdattr & BRD_COOLDOWN) {
    if (vans("目前降溫中, 要開放嗎(y/N)?") != 'y')
        return FULLUPDATE;
    bp->brdattr &= ~BRD_COOLDOWN;
    outs("大家都可以 post 文章了。\n");
    } else {
    getdata(b_lines - 1, 0, "請輸入冷靜理由:", genbuf, 50, DOECHO);
    if (vans("要限制 post 頻率, 降溫嗎(y/N)?") != 'y')
        return FULLUPDATE;
    bp->brdattr |= BRD_COOLDOWN;
    outs("開始冷靜。\n");
    }
    assert(0<=currbid-1 && currbid-1<MAX_BOARD);
    substitute_record(fn_board, bp, sizeof(boardheader_t), currbid);
    post_policelog(bp->brdname, NULL, "冷靜", genbuf, bp->brdattr & BRD_COOLDOWN);
    pressanykey();
    return FULLUPDATE;
}
#endif

static int
b_moved_to_config()
{
    if ((currmode & MODE_BOARD) || HasUserPerm(PERM_SYSOP))
    {
    vmsg("這個功\能已移入看板設定 (i) 去了!");
    return FULLUPDATE;
    }
    return DONOTHING;
}

/* ----------------------------------------------------- */
/* 看板功能表                                            */
/* ----------------------------------------------------- */
/* onekey_size was defined in ../include/pttstruct.h, as ((int)'z') */
const onekey_t read_comms[] = {
    { 1, show_filename }, // Ctrl('A') 
    { 0, NULL }, // Ctrl('B')
    { 0, NULL }, // Ctrl('C')
    { 0, NULL }, // Ctrl('D')
    { 1, lock_post }, // Ctrl('E')
    { 0, NULL }, // Ctrl('F')
#ifdef NO_GAMBLE
    { 0, NULL }, // Ctrl('G')
#else
    { 0, hold_gamble }, // Ctrl('G')
#endif
    { 0, NULL }, // Ctrl('H')
    { 0, board_digest }, // Ctrl('I') KEY_TAB 9
    { 0, NULL }, // Ctrl('J')
    { 0, NULL }, // Ctrl('K')
    { 0, NULL }, // Ctrl('L')
    { 0, NULL }, // Ctrl('M')
    { 0, NULL }, // Ctrl('N')
    { 0, NULL }, // Ctrl('O') // BETTER NOT USE ^O - UNIX not work
    { 0, do_post }, // Ctrl('P')
    { 0, NULL }, // Ctrl('Q')
    { 0, NULL }, // Ctrl('R')
    { 0, NULL }, // Ctrl('S')
    { 0, NULL }, // Ctrl('T')
    { 0, NULL }, // Ctrl('U')
    { 0, do_post_vote }, // Ctrl('V')
    { 0, whereami }, // Ctrl('W')
    { 1, push_bottom }, // Ctrl('X')
    { 0, NULL }, // Ctrl('Y')
    { 0, NULL }, // Ctrl('Z') 26 // 現在給 ZA 用。
    { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL },
    { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL },
    { 1, recommend }, // '%' (m3itoc style)
    { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL },
    { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL },
    { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL },
    { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL },
    { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL },
    { 0, NULL }, { 0, NULL }, { 0, NULL },
    { 0, NULL }, // 'A' 65
    { 0, b_config }, // 'B'
    { 1, do_limitedit }, // 'C'
    { 1, del_range }, // 'D'
    { 1, edit_post }, // 'E'
    { 0, NULL }, // 'F'
    { 0, NULL }, // 'G'
    { 0, b_moved_to_config }, // 'H'
    { 0, b_config }, // 'I'
#ifdef USE_COOLDOWN
    { 0, change_cooldown }, // 'J'
#else
    { 0, NULL }, // 'J'
#endif
    { 0, NULL }, // 'K'
    { 1, solve_post }, // 'L'
    { 0, NULL }, // 'M'
    { 0, NULL }, // 'N'
    { 0, NULL }, // 'O'
    { 0, NULL }, // 'P'
    { 1, view_postinfo }, // 'Q'
    { 0, b_results }, // 'R'
    { 0, NULL }, // 'S'
    { 1, edit_title }, // 'T'
    { 0, NULL }, // 'U'
    { 0, b_vote }, // 'V'
    { 0, b_notes_edit }, // 'W'
    { 1, recommend }, // 'X'
    { 1, recommend_cancel }, // 'Y'
    { 0, NULL }, // 'Z' 90
    { 0, NULL }, { 0, NULL }, { 0, NULL }, { 0, NULL }, 
    { 1, push_bottom }, // '_' 95
    { 0, NULL },
    { 0, NULL }, // 'a' 97
    { 0, b_notes }, // 'b'
    { 1, cite_post }, // 'c'
    { 1, del_post }, // 'd'
    { 0, NULL }, // 'e'
#ifdef NO_GAMBLE
    { 0, NULL }, // 'f'
#else
    { 0, join_gamble }, // 'f'
#endif
    { 1, good_post }, // 'g'
    { 0, b_help }, // 'h'
    { 0, b_config }, // 'i'
    { 0, NULL }, // 'j'
    { 0, NULL }, // 'k'
    { 0, NULL }, // 'l'
    { 1, mark_post }, // 'm'
    { 0, NULL }, // 'n'
    { 0, NULL }, // 'o'
    { 0, NULL }, // 'p'
    { 0, NULL }, // 'q'
    { 1, read_post }, // 'r'
    { 0, do_select }, // 's'
    { 0, NULL }, // 't'
#ifdef OUTJOBSPOOL
    { 0, tar_addqueue }, // 'u'
#else
    { 0, NULL }, // 'u'
#endif
    { 0, NULL }, // 'v'
    { 1, b_call_in }, // 'w'
    { 1, cross_post }, // 'x'
    { 1, reply_post }, // 'y'
    { 0, b_man }, // 'z' 122
};

int
Read(void)
{
    int             mode0 = currutmp->mode;
    int             stat0 = currstat, tmpbid = currutmp->brc_id;
    char            buf[PATHLEN];

    const char *bname = currboard[0] ? currboard : DEFAULT_BOARD;
    if (enter_board(bname) < 0)
    return 0;

    setutmpmode(READING);

    if (currbid != bnote_lastbid &&
    board_note_time && *board_note_time) {
    int mr;

    setbfile(buf, currboard, fn_notes);
    mr = more(buf, NA);
    if(mr == -1)
            *board_note_time=0;
    else if (mr != READ_NEXT)
        pressanykey();
    }
    bnote_lastbid = currbid;
    i_read(READING, currdirect, readtitle, readdoent, read_comms,
       currbid);
    currmode &= ~MODE_POSTCHECKED;
    brc_update();
    setutmpbid(tmpbid);
    currutmp->mode = mode0;
    currstat = stat0;
    return 0;
}

int
ReadSelect(void)
{
    int             mode0 = currutmp->mode;
    int             stat0 = currstat;
    int         changed = 0;

    currstat = SELECT;
    if (do_select() == NEWDIRECT)
    {
    Read();
    changed = 1;
    }
    // need to set utmpbid here...
    // because Read() just restores settings.
    // and 's' directly calls here.
    // so if we don't reset then utmpbid will be out-of-sync.
    // fix someday...
    setutmpbid(0);
    currutmp->mode = mode0;
    currstat = stat0;
    return changed;
}

int
Select(void)
{
    return do_select();
}